最近我有乙個想法,製作乙個小型直譯器,因為需要分析**,所以我去學了點正規表示式。
當我學成歸來,開始動手時,我遇到的第乙個問題就難倒我了——字串的匹配。
**中要用到字串常量,所以需要匹配像c#這樣的,用引號包圍字串,我的第乙個想法是,字串的首位和末尾都是乙個引號,只要匹配這兩個符號就行了,然後我的第乙個**是這樣的:
//字串(無轉義) "hello world"
var str =
"\"hello world\""
;//最終匹配的結果為"hello world"
var mathc = regex.
match
(str,
"\".*\""
);
直接匹配首尾的雙引號,當然上文用來測試的str
變數的匹配是沒問題的,但是,當****現多個引號時,就會出現問題了:
//字串(無轉義) "hello","world"
var str =
"\"hello\",\"world\""
;//預想的結果是匹配兩個字串,"hello"和"world"
var mathces = regex.
matches
(str,
"\".*\""
);
預想匹配str
的結果是"hello"
和"world"
兩個字串,但是當列印匹配的結果時,卻只匹配了乙個字串,即"hello","world"
。匹配並沒有在中間逗號處隔開,而是將中間的引號一併包括在同乙個字串內,也就是「黏在一起」了,這樣的字串在c#的**中是不會出現的。
這是因為正規表示式的貪婪模式(各翻譯可能不同)會盡可能匹配多的字串。
這個問題很快就被我解決了,只需要在正規表示式字串中的『*』符號後面加上乙個『?』以關閉貪婪模式,這樣便能保證在匹配時不會去盡可能匹配多的字串而導致出現字串黏在一起的問題:
var str =
"\"hello\",\"world\""
;//此處用於匹配的字串"\".?\""和原來相比在'*'號後面多了乙個問號 表示關閉了貪婪模式
//此時匹配的結果與預想的結果相同,是 "hello"和"world"兩個字串
var mathces = regex.
matches
(str,
"\".*?\""
);
解決了貪婪模式導致的字串黏在一起的問題後,接下來又出現了乙個新的問題:當字串**現了轉義的引號,那麼匹配將會被這個引號影響:
//字串(無轉義) a:"say:\"hello world\"", b:"say:\"something\""
var str =
"a:\"say:\\\"hello world\\\"\", b:\"say:\\\"something\\\"\""
;//預想的匹配結果是"say:\"hello world\""與"say:\"something\""
var mathces = regex.
matches
(str,
"\".*?\""
);
但事實上如果匹配上文**中的字串str
的話,會出現4個莫名其妙的結果:
可以看到,正規表示式在遇到的第乙個引號處就結束了匹配——即使這個引號在字串中是乙個轉義的存在。如果在c#的**中,這個情況是不會存在的,轉義的引號 任何轉義字元都將被視為字串內的內容。
事實上這個問題我研究了很久,我的第乙個解決方法是:將正則匹配模式中的"."
匹配符換成"(\\\\"|.)"
,即:
var str =
"a:\"say:\\\"hello world\\\"\", b:\"say:\\\"something\\\"\""
;//將匹配模式中的"."換成"(\\\\\"|.)"
//最終的結果將和預想的一樣,匹配到了字串"say:\"hello world\""以及"say:\"something\""
//匹配不會在轉義的引號位置被結束
var mathces = regex.
matches
(str,
"\"(\\\\\"|.)*?\""
);
這樣便能夠解決匹配過程中在轉義的引號處完成匹配的問題,因為在正規表示式對於字串內容的匹配中,會優先將帶有反斜槓的引號,將帶有轉義符的引號匹配到字串的內容中,即將轉義的引號歸類到字串的內容中而不是作為字串的結尾,但這樣子之後乙個新問題又來了,假設匹配遇到了這樣的字串:
//字串(無轉義) "hello\\", "world"
var str =
"\"hello\\\\\", \"world\""
;//預想的匹配結果為兩個字串 "hello\\"和 "world"
var mathces = regex.
matches
(str,
"\"(\\\\\"|.)*?\""
);
可以看到,在字串的hello的位置後面是乙個轉義的反斜槓,緊跟著的是乙個沒有轉義的,用於包圍字串的引號。但如果匹配str
的話,正規表示式會略過hello\末尾的引號,而選擇匹配world前面的引號作為字串末尾的引號。因為在正規表示式看來,hello\末尾的引號的前面有乙個反斜槓轉義符——即使這是乙個轉義過後的反斜槓。
為了解決這個問題,我想了很久,我上網查了很多方法,但大多數的方法都和我上文中的"\"\.*?""
匹配一樣,無法匹配轉義的字元,在我苦思多日(真的是多日,因為我腦子不開竅)無果後,我放棄了正規表示式選擇了迴圈遍歷字串:
var list =
newlist
<
string
>()
;var str =
"\"hello\\\\\", \"world\""
;for
(int i =
0; i < str.length; i++
)var length = i - start +1;
list.
add(str.
substring
(start, length));
}}
在索引位置遇到轉義字元(反斜槓)時,將索引位置的下乙個字元視作轉義字元,索引位置向前推移,不處理,這樣可以避免因為檢查到轉義的引號而導致字串結束,也不會因為檢查到轉義的反斜槓而誤判其他字元。
等等,標題不是說使用正規表示式匹配字串嗎?
沒錯,當我寫完上文**後,我的腦子突然開竅了,我不必執著於去判斷引號是否是為轉義的引號,而是直接去匹配任何轉義的字元即可。將之前的匹配模式中的"\""
更改為"."
,即:
var str =
"\"hello\\\\\", \"world\""
;//與上上文對比,將括號的第乙個匹配模式中的"\""改為"."
//最終的匹配結果與預想的一樣 匹配到了 "hello\\"和"world"
var matches = regex.
matches
(str,
"\"(\\\\.|.)*?\""
);
使用上文的匹配模式,就能夠匹配匹配字串而不會因為轉義的緣故而出現意料之外的匹配。這和正規表示式的匹配原理有關,當正規表示式在匹配過程中匹配到了轉義字元反斜槓,則連同這個轉義字元與下乙個任意字元(換行符除外)一同匹配到字串的內容中嗎,而正規表示式匹配過的內容不會再匹配,所以不會出現上文中將已經轉義過後的反斜槓認作轉義符,而忽略用來結束字串的引號
(文中用到的regex類位於system.dll庫內,需要引用命名空間system.text.regularexpressions)
正規表示式 匹配
字串 void abtr quint32 ab 表示乙個正規表示式 template class bidirectionaliterator class allocator std allocator sub match bidirectionaliterator class match resul...
正規表示式匹配
請實現乙個函式用來匹配包括 和 的正規表示式。模式中的字元 表示任意乙個字元,而 表示它前面的字元可以出現任意次 包含0次 在本題中,匹配是指字串的所有字元匹配整個模式。例如,字串 aaa 與模式 a.a 和 ab ac a 匹配,但是與 aa.a 和 ab a 均不匹配 解法 首先要想到用遞迴處理...
正規表示式匹配
請實現乙個函式用來匹配包括 和 的正規表示式。模式中的字元 表示任意乙個字元,而 表示它前面的字元可以出現任意次 包含0次 在本題中,匹配是指字串的所有字元匹配整個模式。例如,字串 aaa 與模式 a.a 和 ab ac a 匹配,但是與 aa.a 和 ab a 均不匹配 class solutio...