@guoxs
2015-09-12T14:23:20.000000Z
字数 4632
阅读 4037
正则表达式
正则表达式 = 元字符 + 文字
"*" :通配符,任意文本
"?" :任意单个字符
"^":匹配一行的开始
"$"匹配一行的结束
"[ea]":字符组,匹配e或a。例如:H[123456]
匹配<H1><H2><H3><H4><H5><H6>
"-":字符组元字符,表示一个范围。如上例还可表示为<H[1-6>
"|":或,匹配任意字表达式,同样的在字符组里字是一个普通的字符
"\<"、">":元字符序列,单词分界符。
一个字符组只能匹配目标文本中的单个字符,而每个多选结构自身都可能是完整的正则表达式,都可以匹配任意长度的文本。
例子:
"^cat$":只包含cat的行,无多余单词,无空的字符
"^$":匹配一个空行
"^":匹配行开头,无意义
"From|Subject|Date: "与"^(From|Subject|Date): "的区别
"\<cat\>":匹配单词开头,然后是cat,然后是单词结尾
"^1-6":匹配除1到6外的任何字符
".":匹配任意字符的字符组
注意:对于"Iraq",用"q[^u]"是无法匹配的,因为q后无字符,而一个字符组,即使是\排除型字符组,也需要匹配一个字符。
"?":代表可选项,匹配一个或0个。
"+":之后紧邻的元素出现一次或多次
"*":之前紧邻的元素出现任意多次,或者不出现
"...*":匹配尽可能多的次数,如果实在无法匹配,也不要紧
"...+":匹配尽可能多的次数,但如果一次都无法匹配,则报错
"...{min,max}":区间量词
反向引用:匹配与表达式先前部分同样的文本
例如:
"([a-z])([0-9])\1\2":"\1"代表"[a-z]"匹配的内容,而"\2"代表"[0-9]"匹配的内容。
"/":转义符
" "[^"]*" ":匹配引号内的字符串,用来匹配字符串的开头和结尾的引号,用" [^"] "来匹配除双引号外的任意字符,用" * "来表示两引号间可以存在任意数目的非双引号字符。
匹配美元:
"^[+-]?[0-9]+(\.[0-9]*)?$":不能匹配像".369"这样的小数
分组与捕获:
"^([-+]?[0-9]+(\.[0-9]*)?)(CF)$":其中有三个括号,会有三个捕获项。分别保存于$1、$2、$3中。
"(?:)":只分组不捕获。例如"^([-+]?[0-9]+(?:\.[0-9]*)?)(CF)$"只有两个捕获组,"(?:\.[0-9]*)"不在捕获组之内。
$
(?:)
表示非捕获组,使用非捕获组效率更高,因为引擎不需要记忆捕获文本。
常见字符:
"\t":制表符
"\n":换行符
"\r":回车符
"\b":匹配字符串开头或结尾,单词分界符
"\s":匹配任意空白字符,空白包括(空格符、制表符、换行符、回车符、进纸符)
"\S":匹配除"\s"外的任意字符
"\w":等同于"[a-zA-z0-9]","\w+"匹配一个单词
"\W":匹配除"\w"外的任意字符
"\d":[0-9],匹配数字
"\D":"[^0-9]"
例子:
"m/^([-+]?[0-9]+(\.[0-9]*)?)\s*([CF])$/i":i放在m/.../结构后面,不是正则表达式的一部分,而是m/.../的一部分,告诉Perl进行不区分大小写的匹配。
Perl用$variable =~ m/regex/
来判断一个正则表达式是否匹配某个字符串,m表示“匹配”,斜线标注正则表达式边界,整个表达式返回一个布尔值。
使用正则表达式来修改文本
通式:$var =~ s/regex/replacement/
例子:
$var =~ s/\bJeff\b/Jeff/i
:"jeff"这个单词无论大小写都会被替换成"Jeff"。
问:选择12.345这样的浮点数,第三位小数不是0则保留。
$num =~ s/(\.\d\d[1-9]?)\d*/$1/
:前两个"\d\d"表示匹配小数点后两位小数,"d[1-9]?"匹配第三个小数,"(.\d\d[1-9]?)"中捕获的内容保存在$1中,然后去替换原数字。
" .* "表示一组任意字符。
环视功能:不匹配任何字符,只匹配文本中的特定位置。
(?=...)
,如(?=/d)
,表示如果当前位置右边的字符是数字则匹配成功。(?!...)
,子表达式不能匹配右侧文本(?<=...)
。如(?<=/d)
表示当前位置左边有一位数字,则匹配成功(?<!...)
,子表达式不能匹配左侧文本例子:将 Jeffs修改为Jeff's。
s/(?<=\bJeff)(?=s\b)/'/g
:匹配一个位置,该位置在"Jeff"后,在"s"前,在该位置插入'
。
"(?<!\w)(?=\w)|(?<=\w)(?!\w)"等价于"\b",
问:修改数字为"12,345,678"这样的形式。
s/(?<=\d)(?=\d\d\d)+(?!\b)/,/g
不使用逆序环视添加逗号
s/(\d)((\d\d\d)+\d)/$1,$2/g,存在问题,((\d\d\d)+\b)为最终匹配文本,不能作为未匹配文本使用g,会匹配到"12,34567"这样的数字
解决方法:
while($text =~ s/(\d)((\d\d\d)+b\)/$1,$2/g)
{
循环不需任何操作
}
大多数OS系统采用换行符作为一行的终结,而某些操作系统(主要为Windows)采用“回车+换行”结合体表示。
^
$
匹配的不是逻辑行的开头与结尾,而是整个字符的串的开头和结尾的位置,但在强制行锚点匹配模式下,^
$
会从字符串模式切换到逻辑行模式。在Perl中,使用/m选择此模式。
例如:
$text=~ s/^$/<p>/mg:全局将空行替换为<p>标签。
若一行中有空白符则需用"^ *$"或者"^[ \t\r]*$",或者用"$text =~ s/^/s*$/<p>/mg"更为安全。
将E-mail转换为超链接形式
例如:ifriedl@oreilly.com 转化为 <a href="mailto:ifriedl@oreilly.com">ifriedl@oreilly.com</a>。
=> $text =~ s/\b(username regex\@hostname regex)\b/<a href="mailto:$1">$1<\/a>/g;两个反斜杠是转义。添加单词分分界符"\b...\b"是为了避免不完整匹配的情况。
匹配主机与用户名
用户名第一个一般是字母,之后没有太多限制,故可以\w[-.\w]*
匹配,对于主机名,两个点号之间必须要有分隔符,并且结尾一般为特定几中格式,故使用\w+(\.\w+)*\.(com|edu|info)
(实际结尾还有其他格式)。
但是,使用\w
还是有问题的,因为有些系统会匹配一些非ASCII字母,这里可以用[a-zA-Z0-9]
,主机名可能包含连字符,故完整版:[-a-z0-9]+(\.[-a-z0-9]+)*\.(com|edu|info)
。
如果表达式较长,可以使用\x
修饰符。\x
允许用户以“宽松排列”编排表达式,也容许出现#开头的注释。\x
只能用于正则表达式本身,而不能用于replacement字符串。
undef s/; #进入“文件读取”模式
$text = <>; #读入命令行中指定的第一个文件名
$text =~ s/&/&/g; #把基本的HTML...
$text =~ s/</</g; ...字符&、<和>...
$text =~ s/>/>/g; ...进行HTML转义
$text =~ s/^\s*$/<p>/mg; #划分段落
#转化为连接形式...
$text =~ s{
\b
#把地址保存到$1...
(
\w[-.\w]* #username
\@
[-a-z0-9]+(\.[-a-z0-9]+)*\.(com|edu|info) #hostname
)
\b
}{<a href="mailto:$1">$1</a>}gix
print $text; #最后,显示HTML文本
将HTTP URL转化为连接形式
HTTP URL的基本形式是是http://hostname/path,其中path部分可选。
有几点需要注意:
1、 URL中path部分可以是各种字符,使用"[-a-z0-9_:@&?=+,.!/~*`%$]*",在使用Perl时,必须对 @ 和 $进行转义。
2、 path后面不能接"\b",因为URL之后通常会接标点符号,如果末尾有"\b",就不能匹配。
3、末尾的标点不能成为链接的一部分,故可使用否定逆序环视"(?<![.,?!])"即可。
完整程序:
undef s/; #进入“文件读取”模式
$text = <>; #读入命令行中指定的第一个文件名
$text =~ s/&/&/g; #把基本的HTML...
$text =~ s/</</g; ...字符&、<和>...
$text =~ s/>/>/g; ...进行HTML转义
$text =~ s/^\s*$/<p>/mg; #划分段落
#转化为连接形式...
$text =~ s{
\b
#把地址保存到$1...
(
\w[-.\w]* #username
\@
[-a-z0-9]+(\.[-a-z0-9]+)*\.(com|edu|info) #hostname
)
\b
}{<a href="mailto:$1">$1</a>}gix;
#将HTTP URL转化为链接形式...
$text =~ s{
\b
#将URL 保存至$1...
(
http:// [-a-z0-9]+(\.[-a-z0-9]+)*\.(com|edu|info) \b #hostname
(
/ [-a-z0-9_:\@&?=+,.!/~*`%\$]* #path不一定会出现
(?<![.,?!]) #不能以[.,?!]结尾
)?
)
}{{<a href="$1">$1</a>}gix;
print $text; #最后,显示HTML文本
使用Perl的qr
操作符,接收一个正则表达式,生成一个"regex对象",作为变量保存,方便以后调用。
$HostnameRegex = qr/[-a-z0-9]+(\.[-a-z0-9]+)*\.(com|edu|info)/i;
问:为什么有时候
@
$
需要转义?
$
即可表示单词结尾又可以标识变量,但在字符组内部,不能用来表示单次结尾,只能通过转义后标识变量。
@
在Perl中表示数组,使用字符时需要转义。
在Perl中,正则表达式是“基础级别的”,也就是说,基本的运算符可以直接作用于正则表达式,就像+、-作用于数字一样,这样能够减轻使用正则表达式的语法包袱。