正则表达式中的词元
简介
正则表达式中使用的括号不仅将该表达式的元素分组在一起,而且会为找到的与该组条件匹配的任何匹配项指定词元。您可以使用词元匹配同一文本的其他部分。使用词元的一个好处是,词元会记住所匹配的内容,因此您可以在搜索或替换过程中重新调用和重用匹配的文本。
表达式中的每个词元都会被分配一个数字,从 1 开始,从左至右顺序递增。要在表达式的后面引用某个词元,请使用反斜杠并后跟词元编号进行引用。例如,引用表达式中第三组括号生成的词元时,请使用 \3
。
举一个简单的例子,如果您要搜索字符数组中相同顺序的字母,可以捕获第一个字母作为词元,然后搜索紧随其后的匹配字符。在以下所示的表达式中,只要 regexp
与字符数组中的任何非空白字符匹配,(\S)
短语就会创建一个词元。表达式的第二部分 '\1'
查找同一字符第一个实例后面紧跟的第二个实例。
poe = ['While I nodded, nearly napping, ' ... 'suddenly there came a tapping,']; [mat,tok,ext] = regexp(poe, '(\S)\1', 'match', ... 'tokens', 'tokenExtents'); mat
mat = 1×4 cell array {'dd'} {'pp'} {'dd'} {'pp'}
元胞数组 tok
包含多个元胞数组,其中每个元胞数组都包含一个词元。
tok{:}
ans = 1×1 cell array {'d'} ans = 1×1 cell array {'p'} ans = 1×1 cell array {'d'} ans = 1×1 cell array {'p'}
元胞数组 ext
包含多个数值数组,其中每个数值数组都包含一个词元的开始索引和结束索引。
ext{:}
ans = 11 11 ans = 26 26 ans = 35 35 ans = 57 57
另一个示例捕获匹配的 HTML 标记对组(例如 <a>
和 </a>
)以及这些标记之间的文本。用于此示例的表达式为
expr = '<(\w+).*?>.*?</\1>';
该表达式的第一部分 '<(\w+)'
匹配左尖括号 (<
),后跟一个或多个字母、数字或下划线字符。封闭的圆括号捕获左尖括号后面的词元字符。
该表达式的第二部分 '.*?>.*?'
匹配此 HTML 标记的其余部分(一直到 >
的字符),以及可能位于下一个左尖括号之前的任何字符。
最后一部分 '</\1>'
匹配结尾的 HTML 标记中的所有字符。此标记由 </tag>
序列组成,其中 tag
表示捕获为词元的任何字符。
hstr = '<!comment><a name="752507"></a><b>Default</b><br>'; expr = '<(\w+).*?>.*?</\1>'; [mat,tok] = regexp(hstr, expr, 'match', 'tokens'); mat{:}
ans = '<a name="752507"></a>' ans = '<b>Default</b>'
tok{:}
ans = 1×1 cell array {'a'} ans = 1×1 cell array {'b'}
多个词元
下面是一个如何为词元分配值的示例。假定您要搜索以下文本:
andy ted bob jim andrew andy ted mark
您选择使用以下搜索模式来搜索上述文本:
and(y|rew)|(t)e(d)
此模式包含三个生成词元的带括号表达式。当您最后执行搜索时,系统将为每个匹配项生成以下词元。
匹配项 | 词元 1 | 词元 2 |
---|---|---|
|
| |
|
|
|
|
| |
|
| |
|
|
|
只会使用最高级别的括号。例如,如果搜索模式 and(y|rew)
找到文本 andrew
,则会为词元 1 分配值 rew
。但是,如果使用搜索模式 (and(y|rew))
,则会为词元 1 分配值 andrew
。
不匹配的词元
对于正则表达式中指定的词元,如果在待查文本中没有匹配项,regexp
和 regexpi
会返回一个空字符向量 (''
) 作为词元输出,同时还会返回一个范围,用来标记词元在字符串中预期出现的位置。
此处所示的示例会对一个字符向量执行 regexp
,该字符向量指定了从 MATLAB® tempdir
函数返回的路径。正则表达式 expr
包括六个词元设定符,每个设定符代表路径的一部分。第三个设定符 [a-z]+
在字符向量中没有匹配项,因为此部分的路径 Profiles
是以大写字母开头的:
chr = tempdir
chr = 'C:\WINNT\Profiles\bpascal\LOCALS~1\Temp\'
expr = ['([A-Z]:)\\(WINNT)\\([a-z]+)?.*\\' ... '([a-z]+)\\([A-Z]+~\d)\\(Temp)\\']; [tok, ext] = regexp(chr, expr, 'tokens', 'tokenExtents');
如果在文本中未找到词元,regexp
会返回一个空字符向量 (''
) 作为词元以及一个包含词元范围的数值数组。而范围的第一个数字为字符串索引,用于标记词元预期出现的位置;范围的第二个数字为第一个数字减 1。
在此示例中,表达式中指定的第三个词元为空词元,因此返回的第三个词元为空:
tok{:}
ans = 1×6 cell array {'C:'} {'WINNT'} {0×0 char} {'bpascal'} {'LOCALS~1'} {'Temp'}
变量 ext
中返回的第三个词元范围的起始索引设置为 10,这是不含匹配项的词 Profiles
在路径中的起始位置。范围结束索引设置为起始索引减 1,也就是 9:
ext{:}
ans = 1 2 4 8 10 9 19 25 27 34 36 39
替代文本中的词元
在替代文本中使用词元时,请使用 $1
、$2
等符号,而不要使用 \1
、\2
等符号来引用词元。此示例捕获两个词元并颠倒它们的顺序。第一个标文 $1
为 'Norma Jean'
,第二个标文 $2
为 'Baker'
。请注意,regexprep
返回修改后的文本,而不是起始索引的向量。
regexprep('Norma Jean Baker', '(\w+\s\w+)\s(\w+)', '$2, $1')
ans = 'Baker, Norma Jean'
命名捕获
如果您在表达式中使用许多词元,则为这些词元分配名称可能会很有帮助,而不必跟踪为哪个词元分配了哪个词元编号。
引用表达式中的命名词元时,请使用语法 \k<name>
,而不要使用 \1
、\2
等数字:
poe = ['While I nodded, nearly napping, ' ... 'suddenly there came a tapping,']; regexp(poe, '(?<anychar>.)\k<anychar>', 'match')
ans = 1×4 cell array {'dd'} {'pp'} {'dd'} {'pp'}
指定词元还有助于标记 MATLAB 正则表达式函数的输出。当您处理许多文本段时尤其如此。
例如,从多个字符向量中解析街道地址的不同部分。为表达式中的每个词元分配了一个短名称:
chr1 = '134 Main Street, Boulder, CO, 14923'; chr2 = '26 Walnut Road, Topeka, KA, 25384'; chr3 = '847 Industrial Drive, Elizabeth, NJ, 73548'; p1 = '(?<adrs>\d+\s\S+\s(Road|Street|Avenue|Drive))'; p2 = '(?<city>[A-Z][a-z]+)'; p3 = '(?<state>[A-Z]{2})'; p4 = '(?<zip>\d{5})'; expr = [p1 ', ' p2 ', ' p3 ', ' p4];
正如以下结果所展示的,您可以通过命名词元使输出更易于使用:
loc1 = regexp(chr1, expr, 'names')
loc1 = struct with fields: adrs: '134 Main Street' city: 'Boulder' state: 'CO' zip: '14923'
loc2 = regexp(chr2, expr, 'names')
loc2 = struct with fields: adrs: '26 Walnut Road' city: 'Topeka' state: 'KA' zip: '25384'
loc3 = regexp(chr3, expr, 'names')
loc3 = struct with fields: adrs: '847 Industrial Drive' city: 'Elizabeth' state: 'NJ' zip: '73548'