当前位置:编程学习 > JS >>

正则基础之 神奇的转义

答案:1 概述
这或许会是一个让人迷惑,甚至感到混乱的话题,但也正因为如此,才有了讨论的必要。
在正则中,一些具有特殊意义的字符,或是字符序列,被称作元字符,如“?”表示被修饰的子表达式匹配0次或1次,“(?i)”表示忽略大小写的匹配模式等等。而当这些元字符被要求匹配其本身时,就要进行转义处理了。
不同的语言或应用场景下,正则定义方式、元字符出现的位置不同,转义的方式也是林林总总,不一而同。
2 .NET正则中的字符转义
2.1 .NET正则中的转义符
绝大多数语言中,“\”都被作为转义符,用来转义一些具有特殊意义的字符或字符序列,比如“\n”表示换行,“\t”表示水平制表符等。而这样的转义,应用到正则中,又会有一些意想不到的变化。
话题由C#中一个正则问题引出
复制代码 代码如下:

string[] test = new string[]{"\\", "\\\\"};
Regex reg = new Regex("^\\\\$");
foreach (string s in test)
{
richTextBox2.Text += "源字符串: " + s.PadRight(5, ' ') + "匹配结果: " + reg.IsMatch(s) + "\n";
}
/*--------输出--------
源字符串: \ 匹配结果: True
源字符串: \\ 匹配结果: False
*/

对于这个结果,或许有人会感到迷惑,字符串中的“\\”不是代表一个经过转义的“\”字符吗?而“\\\\”不就应该代表两个经过转义的“\”字符吗?那么上面正则匹配的结果应该是第一个为False,第二个为True才对啊?
对于这一问题,直接解释或许不太容易理解,还是换种方式来解释吧。
比如要匹配的字符是这样的
string test = "(";
那么正则如何写呢?因为“(”在正则中是有特殊意义的,所以写正则时必须对它进行转义,也就是“\(”,而在字符串中,要使用“\\” 来表示“\”本身,也就是
Regex reg = new Regex("^\\($");
这个如果理解了,那再把“(”换回“\”,同样道理,在字符串中,要使用“\\” 来表示“\”本身,也就是
Regex reg = new Regex("^\\\\$");
通过这样的分析,可以看出,其实在以字符串形式声明的正则中,“\\\\”匹配的实际上就是单独的一个“\”字符。总结一下它们之间的关系:
输出到控制台或界面的字符串:\
程序中声明的字符串:string test = "\\";
程序中声明的正则:Regex reg = new Regex("^\\\\$");
这样解释是不是已经可以理解了,那么是不是感觉这样很笨拙?是的,在程序中以字符串形式声明的正则,涉及到转义符时就是这样笨拙的。
所以在C#中,还提供了另一种字符串声明方式,在字符串前加个“@”,就可以忽略转义。
复制代码 代码如下:

string[] test = new string[] { @"\", @"\\" };
Regex reg = new Regex(@"^\\$");
foreach (string s in test)
{
richTextBox2.Text += "源字符串: " + s.PadRight(5, ' ') + "匹配结果: " + reg.IsMatch(s) + "\n";
}
/*--------输出--------
源字符串: \ 匹配结果: True
源字符串: \\ 匹配结果: False
*/

这样就简洁多了,也符合通常的理解。
但同时也带来另一个问题,就是双引号的转义处理。在普通的字符串声明中,可以用“\””对双引号进行转义。
string test = "<a href=>但是在字符串前加了“@”后,“\”会被识别为“\”字符本身,这样就不能用“\””对双引号进行转义了,需要用“”””对双引号进行转义。
string test = @"<a href=>而在VB.NET中,正则的定义只有一种形式,与C#中加了“@”后的定义方式是一致的。
复制代码 代码如下:

Dim test As String() = New String() {"\", "\\"}
Dim reg As Regex = New Regex("^\\$")
For Each s As String In test
RichTextBox2.Text += "源字符串:" & s.PadRight(5, " "c) & "匹配结果:" & reg.IsMatch(s) & vbCrLf
Next
'--------输出--------
'源字符串:\ 匹配结果:True
'源字符串:\\ 匹配结果:False
'--------------------

2.2 .NET正则中需要转义的元字符
在MSDN中,以下字符作为正则中的元字符,在匹配其本身时,需要对其进行转义
. $ ^ { [ ( | ) * + ? \
但实际应用中,还要根据实际情况来判断,以上字符可能不需要转义,也可能不止以上字符需要转义。
在正常的正则书写过程中,以上字符的转义通常都能被编写人员正常处理,但是在动态生成正则时,就需要格外的注意,否则变量中包含元字符时,动态生成的正则在编译时可能会抛异常。好在.NET中提供了Regex.Escape方法来处理这一问题。比如根据动态获取的id来提取相应的div标签内容。
string id = Regex.Escape(textBox1.Text);
Regex reg = new Regex(@"(?is)<div(?:(?!id=).)*id=(['""]?)" + id + @"\1[^>]*>(?><div[^>]*>(?<o>)|</div>(?<-o>)|(?:(?!</?div\b).)*)* (?(o)(?!))</div>");
如果不做转义处理,那么动态获取的id如果为“abc(def”这种形式,程序运行过程中就会抛出异常了。
2.3 .NET正则中字符组的转义
在字符组[]中,元字符通常是不需要转义的,甚至于“[”也是不需要转义的。
复制代码 代码如下:

string test = @"the test string: . $ ^ { [ ( | ) * + ? \";
Regex reg = new Regex(@"[.$^{[(|)*+?\\]");
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}
/*--------输出--------
.
$
^
{
[
(

)
*
+
?
\
*/

但是在正则书写时,字符组中的“[”还是建议使用“\[”对其转义的,正则本身就已经是非常抽象,可读性很低的了,如果在字符组中再掺杂进这样不经转义的“[”,会使得可读性更差。而且在出现不正确的嵌套时,可能会导致正则编译异常,以下正则在编译时就会抛异常的。
Regex reg = new Regex(@"[.$^{[(]|)*+?\\]");
然而,.NET的字符组中,是支持集合减法的,在这种正常语法形式下,是允许字符组嵌套的。
复制代码 代码如下:

string test = @"abcdefghijklmnopqrstuvwxyz";
Regex reg = new Regex(@"[a-z-[aeiou]]+");
MatchCollection mc = reg.Matches(test);
foreach (Match m in mc)
{
richTextBox2.Text += m.Value + "\n";
}
/*--------输出--------
bcd
fgh
jklmn
pqrst
vwxyz
*/

这种用法可读性很差,应用也很少见,即使有这种需求也可以通过其它方式实现,了解一下即可,不必深究。
话题再回到转义上,字符组中必须转义的只有“\”,而“[”和“]”出现在字符组中时,也是建议一定做转义处理的。另外有两个字符“^”和“-”,出现在字符组中特定位置时,如果要匹配其本身,也是需要转义的。
“^”出现在字符组开始位置,表示排除型字符组,“[^Char]”也就是匹配除字符组中包含的字符之外的任意一个字符,比如“[^0-9]”表示除数字外的任意一个字符。所以在字符组中,要匹配“^”字符本身,要么不放在字符组开始位置,要么用“\^”进行转义。
Regex reg1 = new Regex(@"[0-9^]");
Regex reg2 = new Regex(@"[\^0-9]");
这两种方式都表达匹配任意一个数字或普通字符“^”。
至于“-”在字符组中特殊性,举一个例子。
复制代码 代码如下:

string test = @"$";
Regex reg = new Regex(@"[#-*%&]");
richTextBox2.Text = "匹配结果:" + reg.IsMatch(test);
/*--------输出--------
匹配结果:True
*/

正则表达式中明明没有“$”,为什么匹配结果会是“True”呢?
[]支持用连字符“-”连接两个字符,来表示一个字符范围。需要注意的是,“-”前后的两个字符是有顺序的,在使用相同的编码时,后面的字符码位应大于或等于前面字符的码位。
复制代码 代码如下:

for (int i = '#'; i <= '*'; i++)
{
richTextBox2.Text += (char)i + "\n";
}
/*--------输出--------
#
$
%
&
'
(
)
*
*/

由于“#”和“*”符合要求,“[#-*]”可以表示一个字符范围,其中就包含了字符“$”,所以上面的正则是可以匹配“$”的,如果只是把“-”当作一个普通字符处理,那么要么换个位置,要么把“-”转义。
Regex reg1 = new Regex(@"[#*%&-]");
Regex reg2 = new Regex(@"[#\-*%&]"

上一个:js正则表达式之$1$2$3$4$5$6$7$8$9属性,返回子匹配的结果
下一个:让URL只允许一些字符的正则表达式

CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,