亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > Regex > 正文

正則基礎之 神奇的轉義

2020-03-16 21:09:05
字體:
來源:轉載
供稿:網友
不同的語言或應用場景下,正則定義方式、元字符出現的位置不同,轉義的方式也是林林總總,不一而同
 
 
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=/"www.test.com/">only a test</a>";
但是在字符串前加了“@”后,“/”會被識別為“/”字符本身,這樣就不能用“/””對雙引號進行轉義了,需要用“”””對雙引號進行轉義。
string test = @"<a href=""www.test.com"">only a test</a>";
而在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(@"[#/-*%&]");
這兩種方式都表示匹配字符組中列舉的字符中的任意一個。
在字符組中,還有一個比較特殊的轉義字符,“/b”出現在正則表達式中一般位置時,表示單詞邊界,也就是一側為組成單詞的字符,另一側不是;而當“/b”出現在字符組中時,表示的是退格符,與普通字符串中出現的“/b”意義是一樣的。
同樣的,還有一個容易被忽視,而且經常被忽視的轉義符“|”,當“|”出現在正則表達式中一般位置時,表示左右兩側“或”的關系;而當“|”出現在字符組中時,它僅僅表示“|”字符本身,沒有任何特殊意義,所以如果不是要匹配“|”本身,而試圖在字符組中使用“|”時,是錯誤的。比如正則表達式“[a|b]”表示的是“a”、“b”、“|”中的任意一個,而不是“a”或“b”。
2.4     .NET正則應用中不可見字符轉義處理
對于一些不可見字符,要在字符串中表示時,需要用轉義字符,比較常見的有“/r”、“/n”、“/t”等等,而這些字符在正則中應用,就變得有些神奇了,先看一段代碼。
復制代碼代碼如下:

string test = "one line. /n another line.";
List<Regex> list = new List<Regex>();
list.Add(new Regex("/n"));
list.Add(new Regex("//n"));
list.Add(new Regex(@"/n"));
list.Add(new Regex(@"//n"));
foreach (Regex reg in list)
{
    richTextBox2.Text += "正則表達式:" + reg.ToString();
    MatchCollection mc = reg.Matches(test);
    foreach (Match m in mc)
    {
        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”的意義是不同的。
復制代碼代碼如下:

string test = "one line. /n another line.";
List<Regex> list = new List<Regex>();
list.Add(new Regex("line/b"));
list.Add(new Regex("line//b"));
list.Add(new Regex(@"line/b"));
list.Add(new Regex(@"line//b"));
foreach (Regex reg in list)
{
     richTextBox2.Text += "正則表達式:" + reg.ToString() + "/n";
     MatchCollection mc = reg.Matches(test);
     foreach (Match m in mc)
     {
          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正則應用中還有一些其它轉義方式,雖然用得不多,但也順便提一下吧。
需求:把字符串中“<”和“>”之間的數字前加上“$”
復制代碼代碼如下:

string test = "one test <123>, another test <321>";
Regex reg = new Regex(@"<(/d+)>");
string result = reg.Replace(test, "<$$1>");
richTextBox2.Text = result;
/*--------輸出--------
one test <$1>, another test <$1>
*/
也許你會驚奇的發現,替換結果不是在數字前加了“$”,而是將所有數字都替換為“$1”了。
為什么會這樣呢,這是因為在替換結構中,“$”是有特殊意義的,在它后面接數字,表示對對應編號捕獲組匹配結果的引用,而有些情況下,需要在替換結果中出現“$”字符本身,但它后面又跟了數字,這時候就需要用“$$”對它進行轉義了。而上面這個例子卻恰恰是由于這種轉義效果導致出現了異常結果,要規避這一問題,可以使替換結果中不出現對捕獲組的引用。
string test = "one test <123>, another test <321>";
Regex reg = new Regex(@"(?<=<)(?=/d+>)");
string result = reg.Replace(test, "$");
richTextBox2.Text = result;
/*--------輸出--------
one test <$123>, another test <$321>
*/

3 JavaScript及Java中的轉義符
JavaScript及Java中正則的轉義符處理,以字符串形式聲明時,基本上都是與.NET中一致的,簡單的介紹一下。
在JavaScript中,以字符串形式聲明正則,與C#中的表現是一樣的,同樣會顯得很笨拙。
復制代碼代碼如下:

<script type="text/javascript">
    var data = ["//", "////"];
    var reg = new RegExp("^////$", "");
    for(var i=0;i<data.length;i++)
    {
        document.write("源字符串:" + data[i]  + "   匹配結果:" + reg.test(data[i]) + "<br />");
    }
</script>
/*--------輸出--------
源字符串:/ 匹配結果:true
源字符串:// 匹配結果:false
*/

JavaScript中雖然沒有提供C#中這種“@”方式的字符串聲明方式,但提供了另一種正則表達式的專有聲明方式。
復制代碼代碼如下:

<script type="text/javascript">
    var data = ["//", "////"];
    var reg = /^//$/;
    for(var i=0;i<data.length;i++)
    {
        document.write("源字符串:" + data[i]  + "   匹配結果:" + reg.test(data[i]) + "<br />");
    }
</script>
/*--------輸出--------
源字符串:/ 匹配結果:true
源字符串:// 匹配結果:false
*/

JavaScript中
var reg = /Expression/igm;
這種聲明方式,一樣可以簡化含有轉義符的正則。
當然,以這種形式聲明正則時,“/”自然也就成為了元字符,正則中出現這一字符時,必須進行轉義處理。比如匹配鏈接中域名的正則
var reg = /http:////:([^//]+)/ig;
很不幸的是,在Java中,目前只提供了一種正則聲明方式,也就是字符串形式的聲明方式
復制代碼代碼如下:

String test[] = new String[]{"//", "////" };
String reg = "^////$";
for(int i=0;i<test.length ;i++)
{
  System.out.println("源字符串:" + test[i] + "   匹配結果:" + Pattern.compile(reg).matcher(test[i]).find());
}
/*--------輸出--------
源字符串:/   匹配結果:true
源字符串://   匹配結果:false
*/

只能期待Java的后續版本能提供這方面的優化了。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
日韩三级影视基地| 91情侣偷在线精品国产| 欧美精品在线免费播放| 国产成人精品一区二区| 亚洲成av人影院在线观看| 国产日韩欧美日韩| 亚洲激情国产精品| 亚洲国产成人av在线| www.日韩欧美| 欧美激情欧美激情在线五月| 亚洲成人在线视频播放| 亚洲欧美日韩直播| 精品久久久久久久久久久久| 国产精品成人国产乱一区| 98精品国产高清在线xxxx天堂| 97色在线视频观看| 国产精品人成电影在线观看| 57pao精品| 欧美成年人视频| 川上优av一区二区线观看| 色久欧美在线视频观看| 亚洲一区制服诱惑| 日韩中文理论片| 欧美日韩一区免费| 国产日韩一区在线| 久久精品国产96久久久香蕉| 日韩在线观看免费| 日韩成人av在线播放| 精品国偷自产在线视频99| 国产精品成人播放| 在线观看国产精品91| 成人午夜在线视频一区| 亚洲网站在线观看| 成人国产在线激情| 久久亚洲精品小早川怜子66| 国产精品美女免费视频| 视频一区视频二区国产精品| 91超碰中文字幕久久精品| 国产亚洲精品久久久久动| 91精品在线看| 国产成人午夜视频网址| 国产一区二区三区中文| 国产色婷婷国产综合在线理论片a| 欧美另类xxx| 欧美性高跟鞋xxxxhd| 欧美性生交xxxxxdddd| 亚洲天堂成人在线| 亚洲男人av电影| 亲爱的老师9免费观看全集电视剧| 久久激情视频久久| 国产精品999999| 亚洲午夜未删减在线观看| 成人黄色免费片| 久久精品国产91精品亚洲| 日韩高清电影免费观看完整版| 亚洲美女免费精品视频在线观看| 亚洲男人天堂久| 国产精品网址在线| 97香蕉久久超级碰碰高清版| 国产精品久久久久久久久久久久久| 日本一本a高清免费不卡| 国产精品亚洲网站| 久久激情视频久久| 亚洲一品av免费观看| 一区二区福利视频| 亚洲电影免费观看高清完整版在线观看| 亚洲国产欧美自拍| 国产亚洲一区二区在线| 久久中国妇女中文字幕| 欧美一乱一性一交一视频| 国产亚洲欧美日韩美女| 亚洲精品720p| 欧美视频一二三| 日韩av免费在线看| 国产精品国语对白| 久久亚洲综合国产精品99麻豆精品福利| 国产综合香蕉五月婷在线| 国内免费久久久久久久久久久| 久久久久国产精品免费| 免费av一区二区| 精品久久久久久亚洲国产300| 国产精品久久久久久久久影视| 亚洲精品一区二区网址| 午夜精品久久久久久久99热浪潮| 蜜月aⅴ免费一区二区三区| 久久精品国产亚洲精品| 92看片淫黄大片看国产片| 国产成人jvid在线播放| 国产婷婷色综合av蜜臀av| 91免费综合在线| 国产一区二区美女视频| 亚洲国产精品久久91精品| 亚洲欧美日韩爽爽影院| 亚洲天堂网在线观看| 国产精品一区二区久久久| 中文字幕欧美精品日韩中文字幕| 亚洲va国产va天堂va久久| 91九色单男在线观看| 国产精品国模在线| 国产最新精品视频| 97超级碰在线看视频免费在线看| 亚洲一区二区三区xxx视频| 亚洲国产精彩中文乱码av| 中文字幕成人在线| 亚洲变态欧美另类捆绑| 国产精品网红直播| 国色天香2019中文字幕在线观看| 精品久久久久久久久久久久| 日韩av在线看| 久久久久久欧美| 欧美巨乳美女视频| 午夜免费日韩视频| 成人亲热视频网站| 欧美日韩国产999| 久久久久国色av免费观看性色| 久久亚洲国产精品成人av秋霞| 亚洲奶大毛多的老太婆| 日韩中文字幕免费| 日本一区二区在线免费播放| 成人在线中文字幕| 2019中文字幕全在线观看| 欧美精品免费在线| 国产精品免费久久久久久| 欧美国产精品va在线观看| 欧美怡春院一区二区三区| 国内久久久精品| 国产精品久久久久久久天堂| 精品国模在线视频| 亚洲人成人99网站| 97人人爽人人喊人人模波多| 亚洲一区二区三区视频| 在线视频日本亚洲性| 国产精品99免视看9| 亚洲精品成人免费| 国产亚洲精品久久久| 国产精品自产拍高潮在线观看| 亚洲大胆美女视频| www欧美xxxx| 日韩精品视频中文在线观看| 中文字幕亚洲欧美| 亚洲男子天堂网| 久久久国产精彩视频美女艺术照福利| 国产午夜一区二区| 国产一区二区三区网站| 欧美日韩视频在线| 国产精品伦子伦免费视频| 成人免费观看网址| 亚洲一区精品电影| 丝袜亚洲另类欧美重口| 欲色天天网综合久久| 欧美精品激情在线观看| 成人a在线观看| 中文字幕免费精品一区高清| 精品国模在线视频| 国产精品白丝jk喷水视频一区| 久久777国产线看观看精品| 国产精品露脸av在线| 国产精品视频999| 精品久久中文字幕| 国产日韩在线看| 国产成人精品日本亚洲专区61| 欧美日韩第一视频| 日韩在线视频免费观看高清中文| 视频在线观看一区二区|