摘 要:正则表达式是一个重要的编程概念。应用正则表达式可以实现很多强大的字符处理功能,有时也可以为常规方法解决起来比较复杂的问题另辟蹊径。本文试图通过比较详尽的示例为没有基础的读者介绍正则表达式的基本概念、用法及其在Matlab中的实现。文末附上几个应用表达式解决实际问题的实例,以利于读者在自己的实践中应用。
关 键 词:正则表达式 Matlab regexp
- 引言
正则表达式就是一个表达式(也是一串字符),它定义了某种字符串模式——利用正则表达式,可以
对大段的文字进行复杂的查找、替换等。本文将以 Matlab 为编程语言,讲解正则表达式的概念和使用方法,
并将在文末以实例说明正则表达式的实践应用。
Matlab 提供的正则表达式函数有三个:
regexp——用于对字符串进行查找,大小写敏感;
regexpi——用于对字符串进行查找,大小写不敏感;
regexprep——用于对字符串进行查找并替换。
简要介绍一下这三个函数,以 regexpi 为例 —— 读者可以先跳过这里,看过全文之后再来看这里。
用法 1:
[start end extents match tokens names] = regexpi(‘str’, ‘expr’)
start 为匹配字符串的起始位置;end 为匹配字符串的终止位置;extents 为扩展内容,和’tokens’指示符
一起用,指示出现 tokens 的位置;match 即找到的匹配字串;tokens 匹配正则表达式中标记(tokens)的字串;
names 为匹配到的命名标记的标记名。
用法 2:
若不需要所有的输出,可以用下面的方式有选择的输出。
[v1 v2 …] = regexpi(‘str’, ‘expr’, ‘q1’, ‘q2’, …)
‘q1’、’q2’ …… 为 ‘start’、’end’、’tokens’、’tokensExtents’、’match’、’names’ 之一,意义与前文相同。v1、
v2…… 的输出顺序与 q1、q2…… 一致。
- 单个字符的匹配
我们先从简单的开始 —— 以 regexpi 函数为例,不区分字符的大小写。假设你要搜索 ‘cat’,搜索用
的正则表达式就是 ‘cat’,这与文本编辑工具里常用的 CTRL+F 是一样的,即正则表达式 ‘cat’ 匹配 ‘cat’、
‘Cat’、’cAt’、’CAt’、’caT’、’CaT’、’cAT’、’CAT’。
为了方便,下面的叙述中字符串和正则表达式的”都省略不写。
2.1 句点符号
. —— 匹配任意一个(只有一个)字符(包括空格)。
假设你在玩英文拼字游戏,想要找出三个字母的单词,而且这些单词必须以 ‘t’ 字母开头,以 ‘n’ 字母结束;另外,有一本英文字典,你可以用正则表达式搜索它的全部内容。要构造出这个正则表达式,你可以使用一个通配符 —— 句点符号 ‘.’ 。这样,完整的表达式就是 t.n,它匹配 tan、ten、tin 和 ton,还匹配 t#n、tpn 甚至 t n,还有其他许多无意义的组合。这是因为句点符号匹配所有字符,包括空格,即:正则表达式 t.n 匹配 ten、tin、ton、t n、tpn、t#n、t@n 等。
Matlab 程序实例:
clear;clc
str=’ten,&8yn2tin6ui>&ton, t n,-356tpn,
t#n,4@).,t@nT&nY’;
pat=’t.n’;
o1=regexpi(str,pat,’start’) %用’start’指定输出 o1 为匹配正则表达式的子串的起始位置
o2=regexpi(str,pat,’end’) %用’end’指定输出 o2 为匹配正则表达式的子串的结束位置
o3=regexpi(str,pat,’match’) %用’match’指定输出 o3 为匹配正则表达式的子串
[o11,o22,o33]=regexpi(str,pat,’start’,’end’,’match’) %同时输出起始位置和字串
输出为:
o1 = 1 10 18 23 31 39 48 51
o2 = 3 12 20 25 33 41 50 53
o3 = ‘ten’ ‘tin’ ‘ton’ ‘t n’ ‘tpn’ ‘t#n’ ‘t@n’ ‘T&n’
o11 = 1 10 18 23 31 39 48 51
o22 = 3 12 20 25 33 41 50 53
o33 = ‘ten’ ‘tin’ ‘ton’ ‘t n’ ‘tpn’ ‘t#n’ ‘t@n’ ‘T&n’
2.2 方括号符号
[oum] —— 匹配方括号中的任意一个。
为了解决句点符号匹配范围过于广泛这一问题,你可以在方括号([])里面指定看来有意义的字符。此时,只有方括号里面指定的字符才参与匹配。也就是说,正则表达式 t[aeio]n 只匹配 tan、Ten、tin 和 toN等。但 Tmn、taen 不匹配,因为在方括号之内你只能匹配单个字符。
Matlab 程序实例:
clear;clc
str=’ten,&8yn2tin6ui>&ton, t n,-356tpn,
t#n,4@).,t@nT&nY’;
pat=’t[aeiou]n’;
[o11,o22,o33]=regexpi(str,pat,’start’,’end’,’match’)
o11 = 1 10 18
o22 = 3 12 20
o33 = ‘ten’ ‘tin’ ‘ton’
2.3 方括号中的连接符
‘[c1-c2]’ —— 匹配从字符 c1 开始到字符 c2 结束的字母序列(按字母表中的顺序)中的任意一个。例如 [a-c] 匹配 a、b、c、A、B、C,即正则表达式 t[a-z]n 匹配 tan、tbn、tcn、tdn、ten、……、txn、tyn、tzn。
Matlab 程序实例:
clear;clc
str=’ten,&8yn2tin6ui>&ton, t n,-356tpn,
t#n,4@).,t@nT&nY’;
pat=’t[a-z]n’;
[o11,o22,o33]=regexpi(str,pat,’start’,’end’,’match’)
o11 = 1 10 18 31
o22 = 3 12 20 33
o33 = ‘ten’ ‘tin’ ‘ton’ ‘tpn’
2.4 特殊字符
.等 —— 即由 ‘\’ 引导的,代表有特殊意义或不能直接输入的单个字符。
在使用 fprintf 函数输出时我们常用 ‘\n’ 来代替回车符,这里也是同样的道理,用 \n 在正则表达式中表示回车符。类似的还有 \t 横向制表符,’*’ 表示 ‘*’ 等。后一种情况用在查询在正则表达式中有语法作用的字符,详见下文。
下面是一些匹配单个字符的转义字符正则表达式及所匹配的值。
\xN 或\x{N} 匹配八进制数值为 N 的字符
\oN 或\o{N} 匹配十六进制数值为 N 的字符
\a Alarm(beep)
\b Backspace
\t 水平 Tab
\n New line
\v 垂直 Tab
\f 换页符
\r 回车符
\e Escape
\c 某些在正则表达式中有语法功能或特殊意义的字符 c,要用 \c 来匹配,而不能直接用 c 匹配,例如 . 用正则表达式 . 匹配,而 \ 用正则表达式 \ 匹配。
Matlab 程序实例:
clear;clc
str=’l.[a-c]i.$.a’;
pat1=’.’;pat2=’.’;
o=regexpi(str,pat1,’match’)
o1=regexpi(str,pat2,’match’)
输出为:
o = ‘l’ ‘.’ ‘[’ ‘a’ ‘-’ ‘c’ ‘]’ ‘i’ ‘.’ ‘$’ ‘.’ ‘a’
o1 = ‘.’ ‘.’ ‘.’
2.5 类表达式
\w、\s 和 \d 等 —— 匹配某一类字符中的一个。
和上面的 \n 等表中的转义字符有所不同,\w、\s、\d 等匹配的不是某个特定的字符,而是某一类字符。具体说明如下:
\w 匹配任意的单个文字字符,相当于 [a-zA-Z0-9_];
\s 匹配任意的单个空白字符,相当于 [\t\f\n\r];
\d 匹配任意单个数字,相当于 [0-9];
\S 匹配除空白符以外的任意单个字符,相当于 [^\t\f\n\r] —— 方括号中的^表示取反;
\W 匹配任意单个字符,相当于 [^a-zA-Z0-9_];
\D 匹配除数字字符外的任意单个字符,相当于 [^0-9]。
Matlab 程序实例:
s=’This city has a population of more than 1,000,000.’;
ptn=’\d’;
regexp(s,ptn,’match’)
输出为:
ans = ‘1’ ‘0’ ‘0’ ‘0’ ‘0’ ‘0’ ‘0’
3.字符串的匹配
3.1 多次匹配
例如需要匹配 ‘ppp’,那么就可以用正则表达式 ‘ppp’,还有一种更简单一点的记法 ‘p{3}’。正则表达式中的 ‘{}’ 用来表示匹配前面的表达式的出现次数,即 ‘p{2,3}’ 匹配 ‘pp’ 和 ‘ppp’。除了 ‘{}’,还有几个
字符,用在表示单个字符的正则表达式后面表示次数,如下所述:
expr? 与 expr 匹配的元素出现 0 或 1 次,相当于{0,1}
expr* 与 expr 匹配的元素出现 0 次或更多,相当于{0,}
expr+ 与 expr 匹配的元素出现 1 次或更多,相当于{1,}
expr{n} 与 expr 匹配的元素出现 n 次,相当于{n,n}
expr{n,} 与 expr 匹配的元素至少出现 n 次
expr{n,m} 与 expr 匹配的元素出现 n 次但不多于 m 次
假设我们要在文本文件中搜索美国的社会安全号码。这个号码的格式是 999-99-9999。用来匹配它的正则表达式为 [0-9]{3}-[0-9]{2}-[0-9]{4}。在正则表达式中,连字符(“-”)有着特殊的意义,因此,它的前面要加上一个转义字符 \。
如果希望连字符号可以出现,也可以不出现 —— 即 999-99-9999 和 999999999 都属于正确的格式。这时,你可以在连字符号后面加上 ‘?’ 数量限定符。这样正则表达式为 [0-9]{3}-?[0-9]{2}-?[0-9]{4}。另外,当我们使用 expr* 时,Matlab 将尽可能的匹配最长的字符子串。如:
str = ‘xyz’;
regexp(hstr, ‘<.*>’, ‘match’)
ans = ‘‘
如果我们希望匹配尽可能短的字符子串时,可以在上面我们使用的字符串后使用 ‘?’,也就是 expr*?,
例如:
str = ‘xyz’;
regexp(hstr, ‘<.*?>’, ‘match’)
ans = ‘’ ‘’ ‘’ ‘’
这个表达式的执行过程是这样的,先执行 expr*,“游标”(如果有的话)就指到了与 expr* 匹配的字符子串的最末端,然后从那里开始再检查下一个字符与后面的表达式是否匹配,如果匹配就继续向前(如果一直成功则返回最长的字符串),如果不匹配则直接返回空。例如:
str = ‘xyz’;
regexp(hstr, ‘<.*+>’, ‘match’)
ans = {}
regexp(hstr, ‘<.*+’, ‘match’)
ans = ‘xyz’
3.2 逻辑运算符
exp|exp2 表示或者满足 exp 或者满足 exp2。
(expr) 将 expr 标记为一组,匹配 expr,并将匹配的字符子串标记起来以供后面使用。关于这部分内容下面还会有更详细介绍。
(?:expr) 表示 expr 为一组,相当于数学表达式中的()
例如:
lstr=’A body or collection of such stories’;
regexp(lstr,’(?:[^aeiou][aeiou]){2,}’,’match’)
ans = ‘tori’
上面的表达式中 {2,} 对 [^aeiou][aeiou] 起作用,如果去掉分组,则只对 [aeiou] 起作用,如下所示:
regexp(lstr,’[^aeiou][aeiou]{2,}’,’match’)
ans = ‘tio’ ‘rie’
(?>expr) expr 中的每个元素是一个分组。(?#expr) 放在(?#和)之间的是注释。如:
regexp(lstr, ‘(?# Match words in caps)[A-Z]\w*’, ‘match’)
ans = ‘A’
expr1|expr2 匹配 expr1 或者 expr2 两者之一即可。
regexp(lstr, ‘[^aeiou\s]o|[^aeiou\s]i’, ‘match’)
ans = ‘bo’ ‘co’ ‘ti’ ‘to’ ‘ri’
^expr 匹配 expr,并且出现在原字符串最前端的子串。expr$ 匹配 expr,并且出现在原字符串最末端的子串。
pi(lstr, ‘^a\w*|\w*s$’, ‘match’)
ans = ‘A’ ‘stories’
\