C#正規表示Group、Lookahead應用
一個字串由小寫英文+數字組成,要將英文字中的的數字加上錢字號($),ex:"a123bkr45" => "a$123bkr45"
如果使用Regex.Replace的Replace(String, String, String)多載該如何做?
此多載的第3個參數可使用[錢字號+數字]代表要替換的Group Value,如$1代表Group[1].Value,至於要換上錢字號可用$$表示
首先想到的pattern是 @"[a-z]([\d]+)[a-z]",要匹配的數字做為一個Group,之後取代時在前面加上錢字號即可
Regex.Matches("a123bkr45", @"[a-z]([\d]+)[a-z]")
看似可以匹配了,但是在取代時就遇到了一個問題:這裡匹配到的是"a123b",如果直接取代會將123前後的a、b一起被取代了,最終得到"$123kr45"
該如何只針對Group[1]也就是123進行取代?
此時將pattern改為 @"([a-z])([\d]+)([a-z])",有了3個額外的Group,數字、數字前後的英文都自成一個Group;而Replace的地方改為"$1$$$2$3",也就是在Group[2](數字的Group)前方加上錢字號
看似只有替換了中間的123,但其實還是整個Match的部分都替換了,只是Group[1]、Group[3]的位置與值都保持不變才有此效果
Regex.Matches("a123bkr45", @"([a-z])([\d]+)([a-z])")
題外話Replace時使用多個Group也可以進行一些字串操作的應用
如 Regex.Replace(s, @"([\S]+)(\s)([\S]+)", "$3$2$1") 可以得到將空格前後的字串兩兩交換位置的效果
這樣看似好像可以了,但接下來換了一個測試案例"a123bkr45z67ac"
再檢查一下匹配,最後的67根本沒被匹配到,替換時最後的67自然也沒被替換為$67
仔細看了下這個匹配結果,應該是67前的z被45匹配掉了,之後匹配不了67前的[a-z]
這時使用Lookahead、Lookbehind:
Lookahead
(?=subexpression)
(?!subexpression)
Lookbehind
(?<=subexpression)
(?<!subexpression)
Lookbehind部分:(?<=subexpression)X,可當作X的前方要有符合subexpression的
Lookahead部分:X(?=subexpression),可當作X的後方要有符合subexpression的
至於!的語法代表"沒有"、"不可有"
Regex.Matches("a123bkr45z67ac", @"(?<=[a-z])([\d]+)(?=[a-z])")
再次調整了語法,可以理解為:要找個數字,且該數字的前後都要有個英文
看似有3個額外的Group,但其實此時前後的英文不會參與匹配,Group只有數字的,這點可以從LinqPad的結果中看出
既然現在英文不參與匹配了,Replace語法也跟著修改
Regex.Replace("a123bkr45z67ac", @"(?<=[a-z])([\d]+)(?=[a-z])", "$$$1")
終於得到了字串 a$123bkr$45z$67ac
參考:
https://docs.microsoft.com/en-us/dotnet/standard/base-types/backtracking-in-regular-expressions