Regular Expression(後面簡稱RE)真的是一個非常難以閱讀的語言,從大學時期初次碰到,到現在當工程師已經5年多,常常碰到都還是要翻找書籍和嘗試很久才能寫出自己想要的東西,這邊就做點簡單的筆記避免之後又忘記了。
範例:
如果今天我有一段Log如下
=================================================
2016-02-25 00:05:50.9017 | INFO | TID:9 |
Request:
Method: GET, RequestUri: ‘http://regularexpression.com.tw/v1/1456329948291/500‘, Version: 1.1, Content: System.Web.Http.WebHost.HttpControllerHandler+LazyStreamContent, Headers:
{
Connection: keep-alive
Accept-Encoding: gzip
Host: regularexpression.com.tw
X-YC-IM-Device-OS: 2
X-YC-IM-Token: Token
X-Forwarded-For: xx.xxx.xxx.xxx
X-Forwarded-Port: 443
X-Forwarded-Proto: https
Process-Start-Time: Time: 2016-02-25 00:05:50.183
Process–End–Time: Time: 2016-02-25 00:05:50.901
Process-Time-Count: Ticks: 7141131, 714 ms
Content-Length: 0
}
=================================================
我想節錄出紅字的部分,我自己解法如下
- 首先因為RE具有將斷行視為一個完整句子的特性,所以要跨行搜尋變的很困難,所以我先會將斷行符號換成自己定義的連結符號,好讓整段Log可以變成一個連貫的字串 ```csharp
content.Replace(Environment.NewLine,”<^^>”);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2. 接著RE Pattern如下
**{% note info %}
/Method:\s(?<mehtod>[^,]*),\sRequestUri:\s'(?<uri>[^']*)',.*?X-YC-IM-Token:\s(?<token>[^<]*)<\^\^>.*?Process-Time-Count:\sTicks:\s[0-9]+,\s(?<processTime>[^<]*)<\^\^>/
{% endnote %}
**
**@解析**Method:\s(?<mehtod>**<span style="color: red;">[^,]</span>***),**
**\s為空白字元,所以等於從Method: 開始到下一個不是**<span style="color: red;">逗號</span>**的字元抓出來儲存到method這個參數之中
@**解析**\sRequestUri:\s'(?<uri>**<span style="color: red;">[^']</span>***)',
從 RequestUri: '開始到下一個不是 <span style="color: red;">' </span>的字元抓出來儲存到uri這個參數之中
@**解析.*?X-YC-IM-Token:\s(?<token><span style="color: red;">[^<]</span>*)<\^\^>**
之後接著N個字元直到出現**X-YC-IM-Token: **將** 之後抓取N個字元直到出現****<span style="color: red;"><</span>為止,**儲存到token。這邊特別提一下這段最前面的.*?,如果我們的Log只有這麼一段沒有太大問題,如果我們的Log是重複上面的格式出現很多串,如果單純只下**.*X-YC-IM-Token也就是少了**<span style="color: red;">問號</span>,那他就會一直抓且跨很多段Log直到最後一個**X-YC-IM-Token**出現為止。
?號有與沒有差別在於貪婪搜尋,有問號表示非貪婪模式,搜尋到第一個符合的即停
3. 後面的符號都出現過也一直重複,所以就不另外說明,在.NET中要取出剛剛用RE儲存的參數寫法如下
```csharp
content = content.Replace(Environment.NewLine,"<^^>");
string pattern = @"Method:\s(?<mehtod>[^,]*),\sRequestUri:\s'(?<uri>[^']*)',.*?X-YC-IM-Token:\s(?<token>[^<]*)<\^\^>.*?Process-Time-Count:\sTicks:\s[0-9]+,\s(?<processTime>[^<]*)<\^\^>";
Regex reg = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = reg.Matches(content);
foreach (Match match in matches)
{
Console.WriteLine(match.Groups["mehtod"].Value);
Console.WriteLine(match.Groups["uri"].Value);
Console.WriteLine(match.Groups["token"].Value);
Console.WriteLine(match.Groups["processTime"].Value);}
以下附錄來源 : 就是愛程式 - 正規表示式 Regular Expression
正規表示式 | 說明及範例 | 比對不成立之字串 |
/a/ | 含字母 “a” 的字串,例如 “ab”, “bac”, “cba” | “xyz” |
/a./ | 含字母 “a” 以及其後任一個字元的字串,例如 “ab”, “bac”(若要比對.,請使用 \.) | “a”, “ba” |
/^xy/ | 以 “xy” 開始的字串,例如 “xyz”, “xyab”(若要比對 ^,請使用 \^) | “axy”, “bxy” |
/xy$/ | 以 “xy” 結尾的字串,例如 “axy”, “abxy”以 “xy” 結尾的字串,例如 “axy”, “abxy” (若要比對 $,請使用 \$) | “xya”, “xyb” |
[13579] | 包含 “1” 或 “3” 或 “5” 或 “7” 或 “9” 的字串,例如:”a3b”, “1xy” | “y2k” |
[0-9] | 含數字之字串 | 不含數字之字串 |
[a-z0-9] | 含數字或小寫字母之字串 | 不含數字及小寫字母之字串 |
[a-zA-Z0-9] | 含數字或字母之字串 | 不含數字及字母之字串 |
b[aeiou]t | “bat”, “bet”, “bit”, “bot”, “but” | “bxt”, “bzt” |
[^0-9] | 不含數字之字串(若要比對 ^,請使用 \^) | 含數字之字串 |
[^aeiouAEIOU] | 不含母音之字串(若要比對 ^,請使用 \^) | 含母音之字串 |
[^\^] | 不含 “^” 之字串,例如 “xyz”, “abc” | “xy^”, “a^bc” |
正規表示式的特定字元 | 說明 | 等效的正規表示式 |
\d | 數字 | [0-9] |
\D | 非數字 | [^0-9] |
\w | 數字、字母、底線 | [a-zA-Z0-9_] |
\W | 非 \w | [^a-zA-Z0-9_] |
\s | 空白字元 | [ \r\t\n\f] |
\S | 非空白字元 | [^ \r\t\n\f] |
正規表示式 | 說明 |
/a?/ | 零或一個 a(若要比對? 字元,請使用 \?) |
/a+/ | 一或多個 a(若要比對+ 字元,請使用 \+) |
/a*/ | 零或多個 a(若要比對* 字元,請使用 \*) |
/a{4}/ | 四個 a |
/a{5,10}/ | 五至十個 a |
/a{5,}/ | 至少五個 a |
/a{,3}/ | 至多三個 a |
/a.{5}b/ | a 和 b中間夾五個(非換行)字元 |
字元 | 說明 | 簡單範例 |
\ | 避開特殊字元 | /A\*/ 可用於比對 “A*”,其中 * 是一個特殊字元,為避開其特殊意義,所以必須加上 “\” |
^ | 比對輸入列的啟始位置 | /^A/ 可比對 “Abcd” 中的 “A”,但不可比對 “aAb” |
$ | 比對輸入列的結束位置 | /A$/ 可比對 “bcdA” 中的 “A”,但不可比對 “aAb” |
* | 比對前一個字元零次或更多次 | /bo*/ 可比對 “Good boook” 中的 “booo”,亦可比對 “Good bk” 中的 “b” |
+ | 比對前一個字元一次或更多次,等效於 {1,} | /a+/ 可比對 “caaandy” 中的 “aaa”,但不可比對 “cndy” |
? | 比對前一個字元零次或一次 | /e?l/ 可比對 “angel” 中的 “el”,也可以比對 “angle” 中的 “l” |
. | 比對任何一個字元(但換行符號不算) | /.n/ 可比對 “nay, an apple is on the tree” 中的 “an” 和 “on”,但不可比對 “nay” |
(x) | 比對 x 並將符合的部分存入一個變數 | /(a*) and (b*)/ 可比對 “aaa and bb” 中的 “aaa” 和 “bb”,並將這兩個比對得到的字串設定至變數 RegExp.$1 和 RegExp.$2。 |
xy | 比對 x 或 y | /a*b*/g 可比對 “aaa and bb” 中的 “aaa” 和 “bb” |
{n} | 比對前一個字元 n 次,n 為一個正整數 | /a{3}/ 可比對 “lllaaalaa” 其中的 “aaa”,但不可比對 “aa” |
{n,} | 比對前一個字元至少 n 次,n 為一個正整數 | /a{3,}/ 可比對 “aa aaa aaaa” 其中的 “aaa” 及 “aaaa”,但不可比對 “aa” |
{n,m} | 比對前一個字元至少 n 次,至多 m 次,m、n 均為正整數 | /a{3,4}/ 可比對 “aa aaa aaaa aaaaa” 其中的 “aaa” 及 “aaaa”,但不可比對 “aa” 及 “aaaaa” |
[xyz] | 比對中括弧內的任一個字元 | /[ecm]/ 可比對 “welcome” 中的 “e” 或 “c” 或 “m” |
[^xyz] | 比對不在中括弧內出現的任一個字元 | /[^ecm]/ 可比對 “welcome” 中的 “w”、”l”、”o”,可見出其與 [xyz] 功能相反。(同時請注意 /^/ 與 [^] 之間功能的不同。) |
[\b] | 比對退位字元(Backspace character) | 可以比對一個 backspace ,也請注意 [\b] 與 \b 之間的差別 |
\b | 比對英文字的邊界,例如空格 | 例如 /\bn\w/ 可以比對 “noonday” 中的 ‘no’ ; /\wy\b/ 可比對 “possibly yesterday.” 中的 ‘ly’ |
\B | 比對非「英文字的邊界」 | 例如, /\w\Bn/ 可以比對 “noonday” 中的 ‘on’ , 另外 /y\B\w/ 可以比對 “possibly yesterday.” 中的 ‘ye’ |
\cX | 比對控制字元(Control character),其中 X 是一個控制字元 | /\cM/ 可以比對 一個字串中的 control-M |
\d | 比對任一個數字,等效於 [0-9] | /[\d]/ 可比對 由 “0” 至 “9” 的任一數字 但其餘如字母等就不可比對 |
\D | 比對任一個非數字,等效於 [^0-9] | /[\D]/ 可比對 “w” “a”… 但不可比對如 “7” “1” 等數字 |
\f | 比對 form-feed | 若是在文字中有發生 “換頁” 的行為 則可以比對成功 |
\n | 比對換行符號 | 若是在文字中有發生 “換行” 的行為 則可以比對成功 |
\r | 比對 carriage return | |
\s | 比對任一個空白字元(White space character),等效於 [ \f\n\r\t\v] | /\s\w*/ 可比對 “A b” 中的 “b” |
\S | 比對任一個非空白字元,等效於 [^ \f\n\r\t\v] | /\S/\w* 可比對 “A b” 中的 “A” |
\t | 比對定位字元(Tab) | |
\v | 比對垂直定位字元(Vertical tab) | |
\w | 比對數字字母字元(Alphanumerical characters)或底線字母(”_”),等效於 [A-Za-z0-9_] | /\w/ 可比對 “.A _!9” 中的 “A”、”_”、”9〃。 |
\W | 比對非「數字字母字元或底線字母」,等效於 [^A-Za-z0-9_] | /\W/ 可比對 “.A _!9” 中的 “.”、” “、”!”,可見其功能與 /\w/ 恰好相反。 |
\o_octal_ | 比對八進位,其中_octal是八進位數目_ | /\oocetal123/ 可比對 與 八進位的ASCII中 “123” 所相對應的字元值。 |
\x_hex_ | 比對十六進位,其中_hex是十六進位數目_ | /\xhex38/ 可比對 與 16進位的ASCII中 “38” 所相對應的字元。 |
好用的Regular expression測試網站 : http://www.rubular.com/
延伸閱讀 : 貪婪與非貪婪效能問題