跳轉到內容

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
華夏公益教科書