字符串模式的使用
引言
Mathematica 中的一般符号字符串模式允许用户进行有效的字符串操作. 下面讨论字符串模式的细节,包括使用和实现的注意事项. 这里强调了帮助系统没有提到的一些问题.
Mathematica 的核心是用来描述一般表达式中模式的强大语言. 该语言用于函数定义,替代,和搜索,使用结构如

、

、

等等.
| Out[1]= |  |
| Out[2]= |  |
| Out[3]= |  |
Mathematica 字符串模式使用相同的结构来描述文本字符串中的模式. 可以把字符串考虑为字符序列并且应用一般
Mathematica 模式原理. 另外还有一些有用的特定字符串模式结构.
| Out[4]= |  |
| Out[5]= |  |
| Out[6]= |  |
规则表达式可以用作指定字符串模式的另一种方法. 该方法更紧凑,但可读性不强.
| Out[7]= |  |
| Out[8]= |  |
| Out[9]= |  |
以下是识别字符串模式的一些函数.
| StringMatchQ["s",patt] | 测试 s 是否与 patt 匹配 |
| StringFreeQ["s",patt] | 测试 s 是否没有与 patt 匹配的子串 |
| StringCases["s",patt] | 给出与 patt 匹配的 s 的子串列表 |
| StringCases["s",lhs->rhs] | 使用 rhs 替换每个 lhs |
| StringPosition["s",patt] | 给出与 patt 匹配的子串位置列表 |
| StringCount["s",patt] | 计算有多少个子串与 patt 匹配 |
| StringReplace["s",lhs->rhs] | 替换每个与 lhs 匹配的子串 |
| StringReplaceList["s",lhs->rhs] | 给出替代 lhs 的所有方式列表 |
| StringSplit["s",patt] | 在每个与 patt 匹配的子串处分隔 s |
| StringSplit["s",lhs->rhs] | 在 lhs 处分隔,并在该位置插入 rhs |
支持字符串模式的函数
一般字符串模式
一般字符串模式从模式对象形式的方式与
Mathematica 中一般模式对象相似. 为了联合几个字符串模式对象,使用
StringExpression 操作符

.
Out[10]//FullForm= |
| |  |
| Out[11]= |  |
出现在字符模式中的对象列表与普通 Mathematica 模式列表紧密匹配. 关于字符串模式,字符串被认为是字符的序列,即可以认为
是 String[a, b, c],而普通模式结构应用于它.
下列对象可以在符号字符串模式中出现:
| "string" | 由字符组成的字符串 |
| _ | 任意单个字符 |
| __ | 具有一个或多个字符的任意子串 |
| ___ | 具有零个或多个字符的任意子串 |
| x_,x__,x___ | 给定名称 x 的子串 |
| x:pattern | 给定名称 x 的模式 |
| pattern.. | 重复一次或多次的模式 |
| pattern... | 重复零次或多次的模式 |
| {patt1,patt2,...} 或 patt1|patt2|... | 与至少一个 匹配的模式 |
| patt/;cond | cond 等于 True 的模式 |
| pattern?test | 对每个字符 test 等于 True 的模式 |
| Whitespace | 空格字符序列 |
| NumberString | 数字字符 |
| DatePattern[spec] | 日期字符 |
| charobj | 表示字符类的一个对象(见下) |
| RegularExpression["regexp"] | 与规则表达式匹配的子串 |
| StringExpression[...] | 任意字符串表达式 |
下列代表字符类:
以下表示字符串中的位置:
下面决定如果有几种可能的情况下,应该使用哪种匹配:
下面讨论关于这些对象的一些重要问题.

、

和

与包含换行符在内的任意字符匹配. 为了与除了换行符(与规则表达式中的 "." 相似)以外的任意字符匹配,使用
Except["\n"]、
Except["\n"].. 和
Except["\n"]....
| Out[12]= |  |
| Out[13]= |  |
| Out[14]= |  |
| Out[15]= |  |
| Out[16]= |  |
| Out[17]= |  |
| Out[18]= |  |
| Out[19]= |  |
这里插入一个lookbehind约束(参见
"规则表达式")以确保只选出由

开头的单词.
| Out[20]= |  |
| Out[21]= |  |
对于字符串模式的 Except 结构采用可以表示单个字符或单个字符类的单一变量.
| Out[22]= |  |
当试图匹配不同长度(如

和

)的模式时,首先尝试最长可能匹配. 为了迫使程序首先尝试最短匹配,用户必须把模式的相关部分封装在
Shortest[ ] 中.
| Out[23]= |  |
| Out[24]= |  |
如果由于某些原因,用户需要在短匹配中使用最长匹配,可以使用
Longest.
| Out[25]= |  |
| Out[26]= |  |
| Out[27]= |  |
规则表达式
规则表达式语法遵从内部的Perl兼容规则表达式库( Perl Compatible Regular Expressions library 或者 PCRE),它与Perl语法相近. (参见 [1] 以获得更多信息和文档). Mathematica 中的一个规则表达式由 RegularExpression 的头部表示.
下面基本元素可以用于规则表达式字符串:
| c | 字符 c |
| . | 除了换行以外的任意字符 |
| [c1c2...] | 任意字符  |
| [c1-c2] | 在范围 - 内的任意字符 |
| [^c1c2...] | 除了 外的任意字符 |
| p* | 重复零次或多次的 p |
| p+ | 重复一次或多次的 p |
| p? | 出现零次或一次的 p |
| p{m,n} | 重复 m 次和 n 次之间的 p |
| p*?,p+?,p?? | 匹配的最短一致字符串 |
| p*+,p++,p?+ | 所有格匹配 |
| (p1p2...) | 与序列 , , ... 匹配的字符串 |
| p1|p2 | 与 或 匹配的字符串 |
下面表示字符类:
| \\d | 数字 0-9 |
| \\D | 非数字 |
| \\s | 空格,换行,tab或其它空格字符 |
| \\S | 非空格字符 |
| \\w | 单词字符 (字母,数字,或者 ) |
| \\W | 非单词字符 |
| [[:class:]] | 位于命名类中的字符 |
| [^[:class:]] | 不位于命名类中的字符 |
可以使用下列命名类: alnum,alpha,ascii,blank,cntrl,digit,graph,lower,print,punct,space,upper,word 和 xdigit.
下面表示字符串中的位置:
| ^ | 字符串(或者行)的开头 |
| $ | 字符串(或者行)的末尾 |
| \\A | 字符串的开头 |
| \\z | 字符串的末尾 |
| \\Z | 字符串的末尾(首先允许单个换行字符) |
| \\b | 词边界 |
| \\B | 除了词边界的任何位置 |
下面对所有规则表达式元素设置选项:
| (?i) | 大写字母和小写字母等价处理(忽略大小写) |
| (?m) | 使 和 与行的开头和结尾匹配(多行模式) |
| (?s) | 允许 与换行符匹配 |
| (?x) | 忽略所有空格键,并且把所有 和 之间的内容作为注释处理 |
| (?-\#c) | 未设置的选项 |
下面是lookahead/lookbehind结构:
| (?=p) | 下面的文本必须与 p 匹配 |
| (?!p) | 下面的文本不能匹配 p |
| (?<= p) | 前面的文本必须匹配 p |
| (?<!p) | 前面的文本必须匹配 p |
下面是关于规则表达式一些问题的讨论.
| Out[28]= |  |
使用所有格量词

,匹配会抓住尽可能多的字符,即使其余的模式需要,也不丢弃任何字符.
| Out[29]= |  |
| Out[30]= |  |
| Out[31]= |  |

对应于十六进制数的字符.
| Out[32]= |  |
用来在规则表达式中进行escape操作的字符的完整列表包括
,
,
,
,
,
,
,
,
,
,
,
,
和
. 例如,为了写出一个文本句号,使用
,为了写出一个文本反斜线,使用
.
在字符类
中,用来进行escape操作的字符的完整列表是
,
,
,
和
.
默认情况下,

和

分别匹配字符串的开头和结尾. 在多行模式下,这些匹配行的开头/结尾.
| Out[33]= |  |
| Out[34]= |  |
在多行模式下,

和

可以用来表示字符串的开头和结尾.
| Out[35]= |  |

修饰符允许用户对规则表达式加入空格和注释,以增强可读性.
| Out[36]= |  |
使用括号包围可以得到命名子模式,如
(subpatt),那么它们就成为编码子模式. 给定的子模式的数目等于从模式的开头计算开放括号数. 可以通过使用
\\n 指代子模式(模式中的第
n
个模式,或者通过规则中的右边中的

.

指的是所有匹配模式.
| Out[37]= |  |
| Out[38]= |  |
| Out[39]= |  |
如果在这些情况下,需要一个后面带有

字符的反斜线,则需要使用一些技巧,并暂时分成两个子字符串.
| Out[40]= |  |
如果需要对一部分模式进行分组,但不想把组作为编号子模式计算,则使用

结构.
| Out[41]= |  |
Lookahead和lookbehind 模式用以确保在不实际把文本包含为匹配的一部分的情况下,模式被匹配.
这里选出跟在字符串

后的单词.
| Out[42]= |  |
这里试图在字符串中选出所有偶数数字,但是它将找到包含部分数字在内的匹配.
| Out[43]= |  |
使用 lookbehind/lookahead,用户可以确保匹配前/后的字符不是数字(注意,lookbehind 测试在这种特殊情况下是多余的.
| Out[44]= |  |
RegularExpression 与 StringExpression 的比较
在可用于一般符号字符串模式和用于规则表达式的不同模式对象之间有紧密的关联. 下面是写为规则表达式和符号字符串模式的模式实例列表.
可用于一般字符串模式,而不能用于规则表达式的模式对象包含条件(
)和可以在匹配中范围一般l Mathematica 代码的模式测试(
).
在规则表达式中的一些特殊结构在一般字符串模式中不直接可用. 这些包括 lookahead/lookbehinds 以及给定长度的重复. 它们可以通过插入一个 RegularExpression 对象,嵌入一个较大的一般字符串模式.
字符串操作函数
下面讨论了在不同字符串操作函数中的一些特殊之处和微妙之处(这些函数的更多信息,请参见参考文献页面).
StringMatchQ
| Out[45]= |  |
| Out[46]= |  |
StringMatchQ 是特别的,因为它允许元字符
和
作为通配符输入(为了后面兼容的原因).
与 Shortest[___] 等价(RegularExpression["(?s).*?"])和
与 Except[CharacterRange["A", "Z"]] (RegularExpression["[^A-Z]"])等价.
| Out[47]= |  |
| Out[48]= |  |
| Out[49]= |  |
注意,技术上说,这里 Shortest 的出现没有任何区别,由于我们只寻找可能的匹配.
如果用户需要范围模式中子模式匹配的字符串的各部,则必须使用 StringCases.
| Out[50]= |  |
StringFreeQ
| Out[51]= |  |
| Out[52]= |  |
StringCases
StringCases 是用来在字符串中寻找模式出现次数,选出子模式并且处理结果的一般目的函数.
| Out[53]= |  |
| Out[54]= |  |
| Out[55]= |  |
| Out[56]= |  |
| Out[57]= |  |
用户也可以把字符串列表作为许多字符串有效处理的第一个变量(参见
"有效匹配的技巧" 以获得更多的讨论信息).
| Out[58]= |  |
| Out[59]= |  |
重叠选项
对于 StringCases,StringPosition 和 StringCount 的 Overlaps 选项处理在找到一个匹配后匹配器如何继续的问题. 它具有三个可能的设置:False,True,或者 All. 对于 StringCases 和 StringCount,默认值是 False,而对于 StringPosition,默认值是 True.
| Out[60]= |  |
| Out[61]= |  |
| Out[62]= |  |
如果在列表中给出多个模式,
Overlaps->True 将使匹配器在进行到下一个字符前,对每个模式每次都从相同的位置开始.
| Out[63]= |  |
| Out[64]= |  |
| Out[65]= |  |
| Out[66]= |  |
StringPosition
| Out[67]= |  |
| Out[68]= |  |
| Out[69]= |  |
| Out[70]= |  |
StringCount
| Out[71]= |  |
| Out[72]= |  |
注意,Overlaps->False 是 StringCount 的默认值.
StringReplace
| Out[73]= |  |
| Out[74]= |  |
当使用规则表达式时,记住右边的

指的是整个匹配子串是方便的.
| Out[75]= |  |
| Out[76]= |  |
| Out[77]= |  |
Out[78]//FullForm= |
| |  |
对于使用联合了一般字符串模式的旧的
选项,这里我们提供了有限的支持,但是该选项不使用并且应该避免使用.
StringReplaceList
| Out[79]= |  |
如果字符串列表作为输入给出,那么输出是结果的嵌套列表.
| Out[80]= |  |
StringSplit
StringSplit 对于把字符串在与模式匹配的分隔符处分成许多字符串是有用的. 默认情况下,分隔在空格符的每次运行之间进行.
| Out[81]= |  |
例如,为了把正常的句子分成单词,用户也需要在分隔符中加入标点符号.
| Out[82]= |  |
| Out[83]= |  |
这些也可以通过把
All 指定为第三个变量包含进来.
| Out[84]= |  |
第三个变量也可以是一个数,它给出字符串分隔的最大子串数.
| Out[85]= |  |
| Out[86]= |  |
| Out[87]= |  |
在输出中,通过把规则作为第二个变量使用,用户可以保留分隔符,或分隔符的各部.
| Out[88]= |  |
| Out[89]= |  |
| Out[90]= |  |
| Out[91]= |  |
用户也可以给出模式和规则列表;与模式匹配的分隔符将不出现在结果中.
Out[92]//InputForm= |
| |  |
对于Perl用户
概况浏览
除了一般字符串模式外,Mathematica 可以是Perl或Python等语言的许多日常普通编程任务的强大替代. 对于熟悉Perl语法,以及Perl进行字符串操作的方式的用户,下面的概要显示如何熟悉 Mathematica 中相同的功能.
下面是涉及类Perl函数的 Mathematica 函数的概括浏览.
下面是关于一些普通Perl结构更详细的讨论.
m/.../
匹配操作符
测试一个字符串是否包含与
匹配的子串. 在 Mathematica 中对于这种简单匹配,可以使用 StringFreeQ.
下面是测试一个字符串是否包含

在

后某处的 Perl 代码片断.
$string = "sdakdb";
if ($string =~ m/a.*b/){
print "Match!";
}
如果接下来需要访问被匹配的字符串的各部,在Perl中使用
,
, ... ,可用的最好的 Mathematica 函数基本上是StringCases.
$res = "ERROR = paper jam";
if ($res =~ m/ERROR = (.*)/){
print "Hey, you should check the $1!";
}
$date = "88/6/13";
($year, $month, $day) = $date =~ m/^(\d+)/(\d+)/(\d+)$/;
| Out[98]= |  |
这与使用

修饰符对一个阵列赋以所有匹配类似.
$text = "128.32.13.117";
@nums = $text =~ m/\d+/g;
| Out[100]= |  |
s/.../.../
$text = "abcagh";
$text =~ s/a./XX/;
| Out[102]= |  |
Perl中的

修饰符进行所有匹配的全局替换.
| Out[103]= |  |
使用计算

修饰符,Perl可以把子模式作为替换操作的部分使用. 在
Mathematica 中,这是很容易实现的.
$text = "13 27 3";
$text =~ s/(\d+)/$1$1/eg
| Out[105]= |  |
split(...)
$text = "ab:cd:efg";
split(/:/, $text)
| Out[107]= |  |
可以在 Perl 和
Mathematica 中可以指定分成的块数目.
| Out[108]= |  |
在模式中用来捕获括号的

,捕获的子串被包括在结果中,可以在
Mathematica 中使用
StringSplit 的第二个变量中的规则来实现. 与Perl相比较,在
Mathematica 中,把函数用于这些子串是容易的.
$text = "test with <tag1>tags</tag1> and <b>more</b>";
split(/<([^>]*)>/, $text)
Out[110]//InputForm= |
| |  |
Out[112]//InputForm= |
| |  |
tr/.../.../
Perl
命令可以使用 Mathematica StringReplace 以及合适的规则列表模拟.
以下是字符

、

和

分别被

、

和

替换的最简单形式.
$text = "abcdef";
$text =~ tr/abc/XYZ/
这里在
Mathematica 中使用
Thread 产生合适的规则.
| Out[114]= |  |
以下例子中,替换列表比字符列表短,因此

、

和

全部被

替代.
$text = "abcdefghi";
$text =~ tr/abcdef/WXYZ/
| Out[116]= |  |
$text = "this and that";
$text =~ tr/a-z/x/
| Out[118]= |  |
使用

修饰符,多于的字符被删除.
$text = "abcdefghi";
$text =~ tr/abcdef/WXYZ/d
| Out[120]= |  |
使用

修饰符,字符列表的补集被采用.
| Out[121]= |  |
| Out[122]= |  |

修饰符挤压到翻译成同样字符的任意字符串序列.
$text = "abbcccddddeeeeeeffeeded";
$text =~ tr/abcde/ABCD/s
| Out[124]= |  |
一些示例
字符串模式的实际应用的简要示例在该章节中讨论.
高亮模式(Highlight Patterns)
| Out[125]= |  |
| Out[126]= |  |
| Out[127]= |  |
HTML解析
字符串模式对于采用原始HTML和从中提取信息非常有用.
| Out[135]= |  |
| Out[136]= |  |
| Out[137]= |  |
寻找货币
| Out[138]= |  |
| Out[139]= |  |
以下是使用规则表达式的相同搜索(注意,必须记住对美元符号进行escape操作).
| Out[134]= |  |
| Out[141]= |  |
在文件中寻找文本
这是一个用来在包含与给定模式匹配的文本中,寻找行的很简单的类grep函数.
| Out[143]= |  |
这里返回

中包含任意数字字符的行数和行.
Out[138]//TableForm= |
| |  |
这里寻找把

作为独立单词的行.
Out[139]//TableForm= |
| |  |
有效匹配的技巧
本章节讨论涉及字符串模式匹配中有效性的问题.
StringExpression 与 RegularExpression 的比较
由于 Mathematica 语法中的字符串模式很快被转化为一个规则表达式,并且被编译和保存,所以比起直接的规则表达式语法来说,使用 Mathematica 语法的开销很小. 一个例外情况是当许多不同模式被多次使用时;在那种情况下,开销可能会变得显著一些.
条件和模式测试
如果一个模式包含
Condition (

)或者
PatternTest (

)语句,一般
Mathematica 计算器必须在匹配过程中调用,因此会降低速度. 如果不使用这样的结构写模式的话,通常速度会更快.
| Out[141]= |  |
| Out[142]= |  |
避免嵌套量词
由于在匹配中使用非确定性有限自动机算法(NFA),涉及嵌套量词(如
和
或规则表达式)的模式可以变得任意缓慢. 这样的模式经常可以被打开成更有效的版本(见 Friedl [2] 以获得更多信息.
避免对函数的多次调用
如果用户在整个字符串长列表中寻找匹配,一次性把整个列表传给字符串函数更为有效,而不使用一些比如
Select 和
StringMatchQ 的操作(具体演示,参见更前面的字典示例). 以下示例产生2000个字符串的列表,每个字符串具有10个字符,并且搜寻以

开头并且包含

作为子串的字符串.
| Out[144]= |  |
| Out[145]= |  |
| Out[146]= |  |
| Out[147]= |  |
把一般表达式搜索重写为字符串搜索
由于字符串匹配算法与用来做一般表达式匹配的 Mathematica 算法不同(例如,字符串匹配可以假定一个有限的字母和平面结构),存在把普通表达式匹配问题转化为字符串匹配问题是有利的情况. 一个典型的情况是把符号的长列表与涉及
和
多次出现的模式匹配.
作为一个例子,假定用户想要寻找至少有4个相同数字的质数(如质数1000000). 使用普通的模式匹配,可以用如下方式完成.
| Out[148]= |  |
通过把整数列表转换为一个字符串,用户可以使用字符串匹配.
| Out[149]= |  |
| Out[150]= |  |
| Out[151]= |  |
| Out[153]= |  |
| Out[154]= |  |
| Out[156]= |  |
| Out[157]= |  |
实现细节
Mathematica 中的字符串模式匹配建立于PCRE顶端(Perl 可兼容规则表达式)的库,作者为 Philip Hazel [1].
在一些情况下,使用前-5.1 Mathematica 算法(例如,当模式只是一个单独的字符串的时候).
任意符号字符串模式首先被翻译为一个规则表达式. 可以使用内部

函数查看翻译.
Out[158]//InputForm= |
| |  |
返回的第一个元素是规则表达式,而其余元素必须对条件、替换规则和命名模式进行操作.
接下来,规则表达式可以使用PCRE编译,然后编译了的版本被存储以备将来相同模式再次出现时使用. 从符号字符模式到规则表达式的转化只出现一次.
模式中的 Mathematica 条件由从PCRE库到 Mathematica 计算器的外部调用命令执行,因此这样会减缓匹配过程.
嵌入一个普通字符串模式的明确的 RegularExpression 对象将连接到一个最终的规则表达式(由括号形如
包围),因此,命名模式的数目将比用户所期待的更为倾斜.
由于PCRE目前不支持在字符码255之外的预调的字符类,单词和字母字符类(如 WordCharacter 和 LetterCharacter)只包括Unicode范围在0-255之间的字符码. 因此,LetterCharacter 和 _?LetterQ 不给出在字符码255之外的等价的结果.
由于相似的 PCRE 限制,忽略大小写的匹配 (例如,使用 IgnoreCase->True)将只应用于在Unicode范围位于0-127之内的字符(即,普通英文字母
-
和
-
).
参考文献
[1] Hazel, P. "PCRE—Perl Compatible Regular Expressions." 2004. www.pcre.org
[2] Friedl, J. E. F. Mastering Regular Expressions, 2nd ed. O'Reilly & Associates, 2002.