XQuery/字符串分析
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> ...
此转换的一种用途是在地图上显示位置。在这里,我们获取观察到的注册号码文件,对其进行解码,按位置分组,并生成一个 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