跳转到内容

XQuery/字符串分析

来自维基教科书,开放的书籍,开放的世界

XQuery analyze-string

[编辑 | 编辑源代码]

XSLT 2.0 包含 analyze-string 结构,它捕获正则表达式中匹配的组(在括号中)。奇怪的是,这在 XQuery 中不可用。可以通过将 XQuery 函数包装在生成的 XSLT 样式表周围来使用 XSLT 结构,即使这似乎相当痛苦。在这个 eXist 安装中,XSLT 引擎是 Saxon 8。

declare function str:analyze-string($string as xs:string, $regex as xs:string,$n as xs:integer ) {
 transform:transform   
(<any/>, 
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0"> 
   <xsl:template match='/' >  
      <xsl:analyze-string regex="{$regex}" select="'{$string}'" > 
         <xsl:matching-substring>
            <xsl:for-each select="1 to {$n}"> 
               <match>  
                   <xsl:value-of select="regex-group(.)"/>  
                </match>  
             </xsl:for-each> 
          </xsl:matching-substring> 
      </xsl:analyze-string>  
   </xsl:template> 
</xsl:stylesheet>,
()
)
};

英国车辆登记号码

[编辑 | 编辑源代码]

为了说明此功能的使用,这里有一个英国车辆牌照的解码器。这些格式经过多次更改,因此脚本必须首先确定使用哪种格式,然后分析号码以找到区域和注册日期的重要代码。模式在 XML 中定义,并定义要使用的正则表达式以及匹配组的含义。

问题:传递重复修饰符失败

import module namespace str = "http://www.cems.uwe.ac.uk/string" at "../lib/string.xqm";

declare variable $patterns := 
<patterns>
   <pattern version="01" regexp="([A-Z][A-Z])(\d\d)[A-Z][A-Z][A-Z]">
            <field>Area</field><field>Date</field>
    </pattern>
   <pattern version="83" regexp="([A-Z])\d+[A-Z]([A-Z][A-Z])">
          <field>Date</field><field>Area</field>
   </pattern>
   <pattern version="63" regexp="([A-Z][A-Z])[A-Z]?\d+([A-Z])">
                <field>Area</field><field>Date</field>
   </pattern>
</patterns>;

declare function local:decode-regno($regno)  {
let $regno := upper-case($regno)
let $regno := replace($regno, " ","")

return 
    for $pattern in $patterns/pattern
    let $regexp := concat("^",$pattern/@regexp,"$")
    return 
       if (matches($regno,$regexp))
       then 
          let $analysis := str:analyze-string($regno,$regexp,count($pattern/field))
          return 
          <regno version="{$pattern/@version}">      
             {for $field at $i in $pattern/field
              let $value := string($analysis[position() = $i])
              let $table := concat($field,$pattern/@version)
              let $value := /CodeList[@id=$table]/Entry[Code=$value]
              return 
                    element {$field} {$value/*} 
             }
          </regno>
       else 
         ()
};

let $regno := request:get-parameter("regno",())
return local:decode-regno($regno)

解码表

[编辑 | 编辑源代码]

单独的表将代码解码为日期范围或区域。这些表是通过 Excel 从 CSV 文件创建的纯 XML。83 年前的区域代码目前不正确。

例如:

<CodeList id="Area83">
	<Entry>
		<Code>AA</Code>
		<Location>Bournemouth</Location>
	</Entry>
	<Entry>
		<Code>AB</Code>
		<Location>Worcester</Location>
	</Entry>
	<Entry>
		<Code>AC</Code>
		<Location>Coventry</Location>
	</Entry>
...

  1. 当前的车牌:WP05LNU
  2. 来自上一系列的一个:L162BAY

位置映射

[编辑 | 编辑源代码]

此转换的一种用途是在地图上显示位置。在这里,我们获取观察到的注册号码文件,对其进行解码,按位置分组,并生成一个 KML 文件,其中位置通过 Google API 进行地理编码。

<NumberList>
    <Regno>H251GBU</Regno>
    <Regno>WRA870Y</Regno>
    <Regno>ENB427T</Regno>
    <Regno>C406OUY</Regno>
    <Regno>N62VNF</Regno>
    <Regno>R895KCV</Regno>
    <Regno>C758HOV</Regno>
    <Regno>H541HEM</Regno>
 ...
(:  this script plots the registration locations of a set of  UK vehicle license plates using kml.  :)

import module namespace geo="http://www.cems.uwe.ac.uk/exist/geo" at "../lib/geo.xqm";

import module namespace str = "http://www.cems.uwe.ac.uk/string" at "../lib/string.xqm";
declare  namespace reg = "http://www.cems.uwe.ac.uk/wiki/reg";

declare option exist:serialize "method=xml media-type=application/vnd.google-earth.kml+xml  indent=yes  omit-xml-declaration=yes"; 
declare variable $reg:icon := "http://maps.google.com/mapfiles/kml/paddle/ltblu-blank.png";
declare variable $reg:patterns := 
<patterns>
   <pattern version="01" regexp="([A-Z][A-Z])(\d\d)[A-Z][A-Z][A-Z]">
            <field>Area</field><field>Date</field>
    </pattern>
   <pattern version="83" regexp="([A-Z])\d+[A-Z]([A-Z][A-Z])">
          <field>Date</field><field>Area</field>
   </pattern>
   <pattern version="63" regexp="([A-Z][A-Z])[A-Z]?\d+([A-Z])">
                <field>Area</field><field>Date</field>
   </pattern>
</patterns>;

declare function reg:decode-regno($regno)  {
let $regno := upper-case($regno)
let $regno := replace($regno, " ","")

return
    for $pattern in $reg:patterns/pattern
    let $regexp := concat("^",$pattern/@regexp,"$")
    return 
       if (matches($regno,$regexp))
       then 
          let $analysis := str:analyze-string($regno,$regexp,count($pattern/field))
          return 
          <regno version="{$pattern/@version}">      
             {for $field at $i in $pattern/field
              let $value := string($analysis[position() = $i])
              let $table := concat($field,$pattern/@version)
              let $value := /CodeList[@id=$table]/Entry[Code=$value]
              return 
                    element {$field} {$value/*} 
             }
          </regno>
       else 
         ()
};

declare function reg:regno-locations($regnos) {
for $regno in  $regnos
let $analysis := reg:decode-regno($regno)
return
   if (exists($analysis//Location))
   then  string($analysis//Location) 
   else ()
};

let $url := request:get-parameter("url",())
let $x := response:set-header('Content-Disposition','inline;filename=regnos.kml;')

return
   <Document>
      <name>Reg nos</name>
      {for $i in (1 to 10)
       return 
       <Style id="size{$i}">
             <IconStyle>
             <scale>{$i}</scale>
             <Icon><href>{$reg:icon}</href> </Icon>     
          </IconStyle>
          </Style>
       }
      {
      let $locations :=   reg:regno-locations(doc($url)//Regno)
      let $max := count($locations)
      for $place in distinct-values($locations)
      let $latlong := geo:geocode(concat($place,',UK'))
      let $count := count($locations[. = $place])
      let $scale := max((round($count div $max  * 10),1))
         order by $count descending
         return         
          <Placemark>
           <name>{$place} ({$count})</name>
           <styleUrl>#size{$scale}</styleUrl>
           <Point><coordinates>{geo:position-as-kml($latlong)}</coordinates></Point>
         </Placemark>
        }
   </Document>

生成地图

短信服务

[编辑 | 编辑源代码]

信息科学与数字媒体系支持一个短信服务,它具有发送和接收短信的功能。该服务由英格兰西部大学布里斯托尔分校支付,所有流量均已记录。

英国车辆牌照号码的解码器是支持移动发起 (MO) 文本消息的演示服务之一。

短信格式为

REG L052

例如 447624803759


以这种格式发送到我们的短信手机号码447624803759的短信会通过一个 PHP 脚本,该脚本允许支持多个短信服务。该脚本使用消息的第一个单词来识别相关服务端点,然后通过 HTTP 调用该端点,将前缀作为code、消息的其余部分作为text以及原手机号码作为from传递。

对于前缀 REG,相关端点是 XQuery 脚本

  http://www.cems.uwe.ac.uk/xmlwiki/regno/smsregno.xq

smsregno.xq 脚本本质上是上面的 parseregno 脚本。

declare option exist:serialize "method=text media-type=text/text";

...

let $regno := request:get-parameter("text",())
let $data :=  local:decode-regno($regno)
return
   concat("Reply: ",
          $regno , 
          " was registered in  ",
          $data/Area/Location,
          " between ", 
          $data/Date/From ,
          " and ", 
          $data/Date/To
         )

然后短信交换机将回复发送到原手机。

待办事项

[编辑 | 编辑源代码]
  • 解决重复修饰符问题(或对 analayze-string 的函数支持)
  • 83 年前的区域代码数据
  • 在 XQuery 中切换实现以替换 PHP 应用程序 - 等待切换到 eXist v2
华夏公益教科书