正则基础之神奇的转义.docx

上传人:b****7 文档编号:9639147 上传时间:2023-02-05 格式:DOCX 页数:14 大小:20.48KB
下载 相关 举报
正则基础之神奇的转义.docx_第1页
第1页 / 共14页
正则基础之神奇的转义.docx_第2页
第2页 / 共14页
正则基础之神奇的转义.docx_第3页
第3页 / 共14页
正则基础之神奇的转义.docx_第4页
第4页 / 共14页
正则基础之神奇的转义.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

正则基础之神奇的转义.docx

《正则基础之神奇的转义.docx》由会员分享,可在线阅读,更多相关《正则基础之神奇的转义.docx(14页珍藏版)》请在冰豆网上搜索。

正则基础之神奇的转义.docx

正则基础之神奇的转义

1概述

这或许会是一个让人迷惑,甚至感到混乱的话题,但也正因为如此,才有了讨论的必要。

在正则中,一些具有特殊意义的字符,或是字符序列,被称作元字符,如“?

”表示被修饰的子表达式匹配0次或1次,“(?

i)”表示忽略大小写的匹配模式等等。

而当这些元字符被要求匹配其本身时,就要进行转义处理了。

不同的语言或应用场景下,正则定义方式、元字符出现的位置不同,转义的方式也是林林总总,不一而同。

2.NET正则中的字符转义

2.1.NET正则中的转义符

绝大多数语言中,“\”都被作为转义符,用来转义一些具有特殊意义的字符或字符序列,比如“\n”表示换行,“\t”表示水平制表符等。

而这样的转义,应用到正则中,又会有一些意想不到的变化。

话题由C#中一个正则问题引出

string[]test=newstring[]{"\\","\\\\"};

Regexreg=newRegex("^\\\\$");

foreach(stringsintest)

{

richTextBox2.Text+="源字符串:

"+s.PadRight(5,'')+"匹配结果:

"+reg.IsMatch(s)+"\n";

}

/*--------输出--------

源字符串:

\匹配结果:

True

源字符串:

\\匹配结果:

False

*/

对于这个结果,或许有人会感到迷惑,字符串中的“\\”不是代表一个经过转义的“\”字符吗?

而“\\\\”不就应该代表两个经过转义的“\”字符吗?

那么上面正则匹配的结果应该是第一个为False,第二个为True才对啊?

对于这一问题,直接解释或许不太容易理解,还是换种方式来解释吧。

比如要匹配的字符是这样的

stringtest="(";

那么正则如何写呢?

因为“(”在正则中是有特殊意义的,所以写正则时必须对它进行转义,也就是“\(”,而在字符串中,要使用“\\”来表示“\”本身,也就是

Regexreg=newRegex("^\\($");

这个如果理解了,那再把“(”换回“\”,同样道理,在字符串中,要使用“\\”来表示“\”本身,也就是

Regexreg=newRegex("^\\\\$");

通过这样的分析,可以看出,其实在以字符串形式声明的正则中,“\\\\”匹配的实际上就是单独的一个“\”字符。

总结一下它们之间的关系:

输出到控制台或界面的字符串:

\

程序中声明的字符串:

stringtest="\\";

程序中声明的正则:

Regexreg=newRegex("^\\\\$");

这样解释是不是已经可以理解了,那么是不是感觉这样很笨拙?

是的,在程序中以字符串形式声明的正则,涉及到转义符时就是这样笨拙的。

所以在C#中,还提供了另一种字符串声明方式,在字符串前加个“@”,就可以忽略转义。

string[]test=newstring[]{@"\",@"\\"};

Regexreg=newRegex(@"^\\$");

foreach(stringsintest)

{

richTextBox2.Text+="源字符串:

"+s.PadRight(5,'')+"匹配结果:

"+reg.IsMatch(s)+"\n";

}

/*--------输出--------

源字符串:

\匹配结果:

True

源字符串:

\\匹配结果:

False

*/

这样就简洁多了,也符合通常的理解。

但同时也带来另一个问题,就是双引号的转义处理。

在普通的字符串声明中,可以用“\””对双引号进行转义。

stringtest="onlyatest";

但是在字符串前加了“@”后,“\”会被识别为“\”字符本身,这样就不能用“\””对双引号进行转义了,需要用“”””对双引号进行转义。

stringtest=@"onlyatest";

而在VB.NET中,正则的定义只有一种形式,与C#中加了“@”后的定义方式是一致的。

DimtestAsString()=NewString(){"\","\\"}

DimregAsRegex=NewRegex("^\\$")

ForEachsAsStringIntest

RichTextBox2.Text+="源字符串:

"&s.PadRight(5,""c)&"匹配结果:

"®.IsMatch(s)&vbCrLf

Next

'--------输出--------

'源字符串:

\匹配结果:

True

'源字符串:

\\匹配结果:

False

'--------------------

2.2.NET正则中需要转义的元字符

在MSDN中,以下字符作为正则中的元字符,在匹配其本身时,需要对其进行转义

.$^{[(|)*+?

\

但实际应用中,还要根据实际情况来判断,以上字符可能不需要转义,也可能不止以上字符需要转义。

在正常的正则书写过程中,以上字符的转义通常都能被编写人员正常处理,但是在动态生成正则时,就需要格外的注意,否则变量中包含元字符时,动态生成的正则在编译时可能会抛异常。

好在.NET中提供了Regex.Escape方法来处理这一问题。

比如根据动态获取的id来提取相应的div标签内容。

stringid=Regex.Escape(textBox1.Text);

Regexreg=newRegex(@"(?

is)

:

(?

!

id=).)*id=(['""]?

)"+id+@"\1[^>]*>(?

>]*>(?

)|

(?

<-o>)|(?

:

(?

!

div\b).)*)*(?

(o)(?

!

))

");

如果不做转义处理,那么动态获取的id如果为“abc(def”这种形式,程序运行过程中就会抛出异常了。

2.3.NET正则中字符组的转义

在字符组[]中,元字符通常是不需要转义的,甚至于“[”也是不需要转义的。

stringtest=@"theteststring:

.$^{[(|)*+?

\";

Regexreg=newRegex(@"[.$^{[(|)*+?

\\]");

MatchCollectionmc=reg.Matches(test);

foreach(Matchminmc)

{

richTextBox2.Text+=m.Value+"\n";

}

/*--------输出--------

.

$

^

{

[

|

*

+

?

\

*/

但是在正则书写时,字符组中的“[”还是建议使用“\[”对其转义的,正则本身就已经是非常抽象,可读性很低的了,如果在字符组中再掺杂进这样不经转义的“[”,会使得可读性更差。

而且在出现不正确的嵌套时,可能会导致正则编译异常,以下正则在编译时就会抛异常的。

Regexreg=newRegex(@"[.$^{[(]|)*+?

\\]");

然而,.NET的字符组中,是支持集合减法的,在这种正常语法形式下,是允许字符组嵌套的。

stringtest=@"abcdefghijklmnopqrstuvwxyz";

Regexreg=newRegex(@"[a-z-[aeiou]]+");

MatchCollectionmc=reg.Matches(test);

foreach(Matchminmc)

{

richTextBox2.Text+=m.Value+"\n";

}

/*--------输出--------

bcd

fgh

jklmn

pqrst

vwxyz

*/

这种用法可读性很差,应用也很少见,即使有这种需求也可以通过其它方式实现,了解一下即可,不必深究。

话题再回到转义上,字符组中必须转义的只有“\”,而“[”和“]”出现在字符组中时,也是建议一定做转义处理的。

另外有两个字符“^”和“-”,出现在字符组中特定位置时,如果要匹配其本身,也是需要转义的。

“^”出现在字符组开始位置,表示排除型字符组,“[^Char]”也就是匹配除字符组中包含的字符之外的任意一个字符,比如“[^0-9]”表示除数字外的任意一个字符。

所以在字符组中,要匹配“^”字符本身,要么不放在字符组开始位置,要么用“\^”进行转义。

Regexreg1=newRegex(@"[0-9^]");

Regexreg2=newRegex(@"[\^0-9]");

这两种方式都表达匹配任意一个数字或普通字符“^”。

至于“-”在字符组中特殊性,举一个例子。

stringtest=@"$";

Regexreg=newRegex(@"[#-*%&]");

richTextBox2.Text="匹配结果:

"+reg.IsMatch(test);

/*--------输出--------

匹配结果:

True

*/

正则表达式中明明没有“$”,为什么匹配结果会是“True”呢?

[]支持用连字符“-”连接两个字符,来表示一个字符范围。

需要注意的是,“-”前后的两个字符是有顺序的,在使用相同的编码时,后面的字符码位应大于或等于前面字符的码位。

for(inti='#';i<='*';i++)

{

richTextBox2.Text+=(char)i+"\n";

}

/*--------输出--------

#

$

%

&

'

*

*/

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

Regexreg1=newRegex(@"[#*%&-]");

Regexreg2=newRegex(@"[#\-*%&]");

这两种方式都表示匹配字符组中列举的字符中的任意一个。

在字符组中,还有一个比较特殊的转义字符,“\b”出现在正则表达式中一般位置时,表示单词边界,也就是一侧为组成单词的字符,另一侧不是;而当“\b”出现在字符组中时,表示的是退格符,与普通字符串中出现的“\b”意义是一样的。

同样的,还有一个容易被忽视,而且经常被忽视的转义符“|”,当“|”出现在正则表达式中一般位置时,表示左右两侧“或”的关系;而当“|”出现在字符组中时,它仅仅表示“|”字符本身,没有任何特殊意义,所以如果不是要匹配“|”本身,而试图在字符组中使用“|”时,是错误的。

比如正则表达式“[a|b]”表示的是“a”、“b”、“|”中的任意一个,而不是“a”或“b”。

2.4.NET正则应用中不可见字符转义处理

对于一些不可见字符,要在字符串中表示时,需要用转义字符,比较常见的有“\r”、“\n”、“\t”等等,而这些字符在正则中应用,就变得有些神奇了,先看一段代码。

stringtest="oneline.\nanotherline.";

Listlist=newList();

list.Add(newRegex("\n"));

list.Add(newRegex("\\n"));

list.Add(newRegex(@"\n"));

list.Add(newRegex(@"\\n"));

foreach(Regexreginlist)

{

richTextBox2.Text+="正则表达式:

"+reg.ToString();

MatchCollectionmc=reg.Matches(test);

foreach(Matchminmc)

{

richTextBox2.Text+="匹配内容:

"+m.Value+"匹配起始位置:

"+m.Index+"匹配长度:

"+m.Length;

}

richTextBox2.Text+="匹配总数:

"+reg.Matches(test).Count+"\n----------------\n";

}

/*--------输出--------

正则表达式:

匹配内容:

匹配起始位置:

10匹配长度:

1匹配总数:

1

----------------

正则表达式:

\n匹配内容:

匹配起始位置:

10匹配长度:

1匹配总数:

1

----------------

正则表达式:

\n匹配内容:

匹配起始位置:

10匹配长度:

1匹配总数:

1

----------------

正则表达式:

\\n匹配总数:

0

----------------

*/

可以看到,前三种写法,输出的正则虽不同,但执行结果却是完全相同的,只有最后一种是没有匹配的。

正则表达式一Regex("\n"),其实就是以普通字符串形式来声明正则的,与用Regex("a")来匹配字符“a”是同样的道理,是不经过正则引擎转义的。

正则表达式二Regex("\\n"),是以正则表达式形式来声明正则的,正如正则中的“\\\\”就等同于字符串中的“\\”一样,正则中的“\\n”就等同于字符串中的“\n”,是经过正则引擎转义的。

正则表达式三Regex(@"\n"),与正则表达式二等价,是字符串前加“@”的写法。

正则表达式四Regex(@"\\n"),其实这个表示的是字符“\”后面跟一个字符“n”,是两个字符,这个在源字符串中自然是找不到匹配项的。

这里需要特别注意的还是“\b”,不同的声明方式,“\b”的意义是不同的。

stringtest="oneline.\nanotherline.";

Listlist=newList();

list.Add(newRegex("line\b"));

list.Add(newRegex("line\\b"));

list.Add(newRegex(@"line\b"));

list.Add(newRegex(@"line\\b"));

foreach(Regexreginlist)

{

richTextBox2.Text+="正则表达式:

"+reg.ToString()+"\n";

MatchCollectionmc=reg.Matches(test);

foreach(Matchminmc)

{

richTextBox2.Text+="匹配内容:

"+m.Value+"匹配起始位置:

"+m.Index+"匹配长度:

"+m.Length+"\n";

}

richTextBox2.Text+="匹配总数:

"+reg.Matches(test).Count+"\n----------------\n";

}

/*--------输出--------

正则表达式:

line_

匹配总数:

0

----------------

正则表达式:

line\b

匹配内容:

line匹配起始位置:

4匹配长度:

4

匹配内容:

line匹配起始位置:

20匹配长度:

4

匹配总数:

2

----------------

正则表达式:

line\b

匹配内容:

line匹配起始位置:

4匹配长度:

4

匹配内容:

line匹配起始位置:

20匹配长度:

4

匹配总数:

2

----------------

正则表达式:

line\\b

匹配总数:

0

----------------

*/

正则表达式一Regex("line\b"),这里的“\b”是退格符,是不经过正则引擎转义的。

源字符串中是没有的,所以匹配结果为0。

正则表达式二Regex("line\\b"),是以正则表达式形式来声明正则的,这里的“\\b”是单词边界,是经过正则引擎转义的。

正则表达式三Regex(@"line\b"),与正则表达式二等价,指单词边界。

正则表达式四Regex(@"line\\b"),其实这个表示的是字符“\”后面跟一个字符“b”,是两个字符,这个在源字符串中自然是找不到匹配项的。

2.5.NET正则应用中其它转义处理

.NET正则应用中还有一些其它转义方式,虽然用得不多,但也顺便提一下吧。

需求:

把字符串中“<”和“>”之间的数字前加上“$”

stringtest="onetest<123>,anothertest<321>";

Regexreg=newRegex(@"<(\d+)>");

stringresult=reg.Replace(test,"<$$1>");

richTextBox2.Text=result;

/*--------输出--------

onetest<$1>,anothertest<$1>

*/

也许你会惊奇的发现,替换结果不是在数字前加了“$”,而是将所有数字都替换为“$1”了。

为什么会这样呢,这是因为在替换结构中,“$”是有特殊意义的,在它后面接数字,表示对对应编号捕获组匹配结果的引用,而有些情况下,需要在替换结果中出现“$”字符本身,但它后面又跟了数字,这时候就需要用“$$”对它进行转义了。

而上面这个例子却恰恰是由于这种转义效果导致出现了异常结果,要规避这一问题,可以使替换结果中不出现对捕获组的引用。

stringtest="onetest<123>,anothertest<321>";

Regexreg=newRegex(@"(?

<=<)(?

=\d+>)");

stringresult=reg.Replace(test,"$");

richTextBox2.Text=result;

/*--------输出--------

onetest<$123>,anothertest<$321>

*/

3JavaScript及Java中的转义符

JavaScript及Java中正则的转义符处理,以字符串形式声明时,基本上都是与.NET中一致的,简单的介绍一下。

在JavaScript中,以字符串形式声明正则,与C#中的表现是一样的,同样会显得很笨拙。

vardata=["\\","\\\\"];

varreg=newRegExp("^\\\\$","");

for(vari=0;i

{

document.write("源字符串:

"+data[i]+"匹配结果:

"+reg.test(data[i])+"
");

}

/*--------输出--------

源字符串:

\匹配结果:

true

源字符串:

\\匹配结果:

false

*/

JavaScript中虽然没有提供C#中这种“@”方式的字符串声明方式,但提供了另一种正则表达式的专有声明方式。

vardata=["\\","\\\\"];

varreg=/^\\$/;

for(vari=0;i

{

document.write("源字符串:

"+data[i]+"匹配结果:

"+reg.test(data[i])+"
");

}

/*--------输出--------

源字符串:

\匹配结果:

true

源字符串:

\\匹配结果:

false

*/

JavaScript中

varreg=/Expression/igm;

这种声明方式,一样可以简化含有转义符的正则。

当然,以这种形式声明正则时,“/”自然也就成为了元字符,正则中出现这一字符时,必须进行转义处理。

比如匹配链接中域名的正则

varreg=/http:

\/\/:

([^\/]+)/ig;

很不幸的是,在Java中,目前只提供了一种正则声明方式,也就是字符串形式的声明方式

Stringtest[]=newString[]{"\\","\\\\"};

Stringreg="^\\\\$";

for(inti=0;i

{

System.out.println("源字符串:

"+test[i]+"匹配结果:

"+Ppile(reg).matcher(test[i]).find());

}

/*--------输出--------

源字符串:

\匹配结果:

true

源字符串:

\\匹配结果:

false

*/

只能期待Java的后续版本能提供这方面的优化了。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工作范文 > 演讲主持

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1