[NAME]
ALL.dao.type.string.pattern

[TITLE]
字符串模式匹配

[DESCRIPTION]


 0.1   介绍  

正则表达式是一个用单个字符串表示的句法规则, 用以描述或者匹配符合该规则的所有字符串。 正则表
达式主要被用来进行字符串操作, 如在字符串里查找,提取或替换符合某规则的子字符串。

 0.2   字符类  

一个字符类表示从属某类的所有字符: 
  *  x : 普通字符表示它自己,特殊字符^$()%.[]*+-?{}<>不在此之列;
  *  . : 点表示所有字符;
  *  %a : 所有字母符号;
  *  %s : 所有空白字符;
  *  %k : 所有控制字符;
  *  %p : 所有标点符号;
  *  %d : 所有数字;
  *  %x : 所有十六进制数字;
  *  %c : 所有小写字母;
  *  %e : 所有中日韩文字;
  *  %w : 所有字母,数字加'_';
  *  %A : 所有非字母字符,%a的补集; 
  *  %S : 所有非空白字符,%s的补集;
  *  %K : 所有非控制字符,%k的补集;
  *  %P : 所有非标点符号字符,%p的补集;
  *  %D : 所有非数字,%d的补集;
  *  %X : 所有非十六进制数字的字符,%x的补集;
  *  %C : 大写字母;
  *  %E : %e的补集;
  *  %W : %w的补集;
  *  %x : 表示字符x,   这里x是一非数字,非字母的字符;  x也可以是字母字符,如果它不是b或者B,
     也不表示某个字符类;
  *  [set] : 表示set中所有字符的并集; set可以以x-y的形式包含一从xy区间上的所有字符; 上面
     的字符类也可出现在set里;
  *  [^set] : [set]的补集。 


 0.3   模式项  

模式项可以是: 
  *  单个字符类;
  *  ^ : 匹配字符串开头;
  *  $ : 匹配字符串结尾;
  *  %n : 匹配第n个模式组的子字符串;n可以是一个或多个数字;
  *  %bxy : 匹配一平衡配对的字符xy; 这里平衡表示,从相同的匹配位置开始,被匹配的子字符串必
     须包含相同且最少数目的 xy; 类似于Lua的字符串模式语法;
  *  %B{pattern1}{pattern2} : 匹配一平衡配对的模式pattern1pattern2; 类似于%bxy 0.4  模式项重复 
模式项e可以选择性的被忽略或重复匹配,规则如下: 
  *  e? : 匹配零次或一次;
  *  e* : 匹配零次或任意次数;
  *  e+ : 匹配一次或多次;
  *  e{n} : 匹配n次;
  *  e{n,} : 匹配至少n次;
  *  e{,n} : 匹配至多n次;
  *  e{n,m} : 匹配至少n次,且最多m次; 

 0.5  模式组和扑获 

在正则表达式里,可以用括号将一个或多个模式项括起来形成一个子模式,即模式组(group)。 一个模式
组里可包含多个可选子模式,以|分开。 如果用(|pattern)(pattern|)包含一个空的可选子模式, 那么
这个模式组可在字符串匹配过程中被跳过。 如果正则表达式里含有多个模式组,那么按模式组的左括号
所出现的顺序, 每个模式组都会自动获得一个标号。例如,在(%a+)%s*(%d+(%a+))里, 第一个(%a+)将拥
有标号1,(%d+(%a+))拥有标号2, 而第二个(%a+)将拥有标号3。 为了方便起见,整个正则表达式所表达的
模式也被自动定义为一个模式组, 标号为0。

当一个字符串被匹配到一个正则表达式时,那些与其中的模式组相匹配的 子字符串将被标记(扑获),以
便于被引用或提取。 如在进行字符串匹配或替换时,%n可被用作表示 第n个模式组所匹配的子字符串。

当一个正则表达式可以有多种方式匹配到一个字符串里起始于同一个下标的子字符串时, 匹配长度最长
的匹配方式将被选中,并返回相应的结果。 匹配长度将被定义为该表达式里所有模式组所匹配的子字符
串的长度的和。 根据这种定义,如果需要给予某个模式组更高的匹配优先权,那么可以 给该模式组增加
更多层括号。 例如(%d%w*)(%w*%d)可以有两种方式匹配到1a2, 一种是将1a匹配给(%d%w*)2匹配给(%
w*%d); 另一种是将1匹配给(%d%w*)a2匹配给(%w*%d)。 如果在(%w*%d)外再添加一层括号,即(%d%w*)
((%w*%d)), 那么它与1a2的匹配将变得唯一,也就是将a匹配到后面的组里。

 0.6  字符串函数 

象Lua里一样,正则表达式的功能需要通过字符串的成员方法使用。 正则表达式需要以字符串的形式作为
参数传递给那些方法。 每个正则表达式字符串都在第一次使用时被编译为一内部表达结构, 并与之一一
对应。每当正则表达式字符串被使用时,其相应的内部表达结构 将被取用,以避免多次编译正则表达式。
实际上,每个正则表达式字符串在每一个虚拟进程里仅被编译一次。

道字符串类型提供了以下基于模式匹配的方法: 
     
   1  fetch( invar self: string, pattern: string, group = 0, start = 0, end = -1 )
   2      => string
   3  match( invar self: string, pattern: string, group = 0, start = 0, end = -1 )
   4      => tuple<start:int,end:int>|none
   5  change( invar self: string, pattern: string, target: string, index = 0, 
   6      start = 0, end = -1 ) => string
   7  capture( invar self: string, pattern: string, start = 0, end = -1 ) => list<string>
   8  extract( invar self: string, pattern: string, 
   9      mtype: enum<both,matched,unmatched> = $matched ) => list<string>
  10  scan( invar self: string, pattern: string, start = 0, end = -1 )
  11      [start: int, end: int, state: enum<unmatched,matched> => none|@V]
  12      => list<@V>
     


 0.6.1   fetch(invar self:string,pattern:string,group=0,start=0,end=-1)=>string  
     
   1  fetch( invar self: string, pattern: string, group = 0, start = 0, end = -1 )
   2      => string
     
提取字符串中匹配"pattern"模式中第"group"组模式所匹配的子字符串。
只用"start"和"end"之间的字字符串被查找匹配。
负下标从字符串末尾开始算(也即负下标数加字符串长度所表示的下标)。

例子: 
     
   1  var S1 = "ABC123DEF456GHI"
   2  var S2 = S1.fetch( "%d+" )          # S2 = "123"
   3  var S3 = S1.fetch( "%d+(%a+)", 1 )  # S3 = "DEF"
   4  var S4 = S1.fetch( "%d+", 0, 6 )    # S4 = "456"
     


 0.6.2   match(invar self:string,pattern:string,group=0,start=0,end=-1)=>...  
     
   1  match( invar self: string, pattern: string, group = 0, start = 0, end = -1 )
   2      => tuple<start:int,end:int>|none
     
查找字符串中"pattern"模式所匹配的子字符串。
如果有匹配,所匹配的子字符串的首字母和末字母的下标将以元组返回。 如果没有匹配,返回空值"none
"。
参数"start"和"end"的含义跟string::fetch()里的一样。

例子: 
     
   1  var S1 = "ABC123DEF(456)GHI"
   2  var M2 = S1.match( "%d+" )          # M2 = (start=3,end=5); substring: "123"
   3  var M3 = S1.match( "%b()" )         # M3 = (start=9,end=13); substring: "(456)"
   4  var M4 = S1.match( "%b{}" )         # M4 = none;
   5  var M5 = S1.match( "%d+(%a+)", 1 )  # M5 = (start=6,end=8); substring: "DEF"
     


 0.6.3   change(invar self:string,pat:string,tar:string,index=0,start=0,end=-1)=>string  
     
   1  change( invar self: string, pattern: string, target: string, index = 0, 
   2      start = 0, end = -1 ) => string
     
将字符串里匹配"pattern"模式的子字符串替换为"target"目标字符串。 并将替换后的结果以新的字符
串形式返回。
目标字符串"target"里可包含对"pattern"模式里模式组的引用。
如果"index"为零,所有匹配的部分都将被替换,否则仅第"index"个匹配将被替换。
参数"start"和"end"的含义跟string::fetch()里的一样。

例子: 
     
   1  var S1 = "ABC123DEF456GHI"
   2  var S2 = S.change( "%d+", ";" )          # S2 = "ABC;DEF;GHI"
   3  var S3 = S.change( "(%d+)", "<%1>", 1 )  # S3 = "ABC<123>DEF456GHI"
     


 0.6.4   capture(invar self:string,pattern:string,start=0,end=-1)=>list<string>  
     
   1  capture( invar self: string, pattern: string, start = 0, end = -1 ) => list<string>
     
提取字符串里跟"pattern"模式及其子模式组匹配的子字符串,并将这些子字符串以 字符串列表的形式
返回。这个列表里第i个字符串对应于第i个子模式组。
值得注意的是,子模式组总是从一开始标记,而零保留为代表整个模式。
参数"start"和"end"的含义跟string::fetch()里的一样。

例子: 
     
   1  var S1 = "ABC123DEF456GHI"
   2  var L1 = S1.capture( "%d+" )        # L1 = { "123" }
   3  var L2 = S1.capture( "%d+ (%a+)" )  # L2 = { "123DEF", "DEF" }
     


 0.6.5   extract(invar self:string,pattern:string,mtype:enum<...>=$matched)=>...  
     
   1  extract( invar self: string, pattern: string, 
   2      mtype: enum<both,matched,unmatched> = $matched ) => list<string>
     
提取字符串中匹配,或匹配间,或两者的子字符串,并作字符串列表返回。

例子: 
     
   1  var S1 = "ABC123DEF456GHI"
   2  var L1 = S1.extract( "%d+" )   # L1 = { "123", "456" }
   3  var L2 = S2.extract( "%d+", $unmatched )  # L2 = { "ABC", "DEF", "GHI" }
     


 0.6.6   scan(invar self:string,pattern:string,start=0,end=-1)[...]=>list<@V>  
     
   1  scan( invar self: string, pattern: string, start = 0, end = -1 )
   2      [start: int, end: int, state: enum<unmatched,matched> => none|@V]
   3      => list<@V>
     
用"pattern"模式扫瞄字符串,并为每个匹配的子字符串,和匹配之间的子字符串 调用附加的代码块。
子字符串的开始和结束下标可以作为参数传递个代码块。
参数"start"和"end"的含义跟string::fetch()里的一样。

例子: 
     
   1  var S1 = "ABC123DEF"
   2  S1.scan( "%d+" ) { [start, end, state]
   3      io.writeln( start, end, S1[start:end], state )
   4  }
   5  # Output:
   6  # 0 2 ABC $unmatched(0)
   7  # 3 5 123 $matched(1)
   8  # 6 8 DEF $unmatched(0)