進階4: RegExp和sscanf

正規表示式

Regular expression應該是寫shell script最好用的工具之一了,無論是要做分類或判斷pattern,都可以用正規表示式來達成。C語言沒有一個好用的正規表示式工具可以使用,所以碰到字串分類必須得要倚靠sscanf。先給一個簡單的例子,假設要分類的字串是:

/usr/sbin/sendmail - 0 errors, 4 warnings

我們希望將前面的絕對路徑和後面兩個數字給拔出來,在C語言透過sscanf是這麼做的:

const char *line = "/usr/sbin/sendmail - 0 errors, 4 warnings";
int e, w;
char path[256];

sscanf(line, "%[^ ] - %d errors, %d warnings", path, &e, &w);
printf("path = %s , e = %d , w = %d\n", path, e, w);

透過sscanf可以簡單地達成這個目的。務必提醒一下,%[^ ]這個寫法很重要,不能只單純的%s,因為空格也是字串的一部分,sscanf會不知道空格要停下來,那麼path就會變成/usr/sbin/sendmail - 0 errors, 4 warnings

Python並沒有提供sscanf這樣的工具,但提供了更有威力的正規表示式,sscanf能夠做到的,正規表示式一定做得到,反之不成立。以下是python透過正規表示式的例子:

import re
pat = '(\S+) - (\d+) errors, (\d+) warnings'
line = '/usr/sbin/sendmail - 0 errors, 4 warnings'
match = re.findall(pat, line)
print('path =', match[0][0], ', e =', match[0][1], ', w =', match[0][2])

上面例子中match會是一個列表,列表的每一個mamber裝了所有對應的tuple,長相是:

[('/usr/sbin/sendmail', '0', '4')]

因為列表只有一個成員,所以要先[0],接著才是pattern對應的順序,這點請小心。python提供了許多正規表示式可以使用的功能,findall是其中一種,其他還有search和match,都滿常用到的。但講下去會離C語言越來越遠,所以點到為止。這邊整理一個表格,是sscanf對應的正規表示式寫法:

sscanf Token Regular Expression
%c .
%5c .{5}
%d [-+]?\d+
%e, %E, %f, %g [-+]?(\d+(.\d*)?\ .\d+)([eE][-+]?\d+)?
%i [-+]?(0[xX][\dA-Fa-f]+\ 0[0-7]*\ \d+)
%o [-+]?[0-7]+
%s \S+
%u \d+
%x, %X [-+]?(0[xX])?[\dA-Fa-f]+

透過正規表示式就可以完成所有sscanf的功能,但對C語言使用者來說,正規表示式不夠直覺,使用起來也卡卡的,這邊提供一個速解,但無法像正規表示式一般強大,不過對於一般日常也很夠用了。

input = '1 3.0 false hello'
(a, b, c, d) = [t(s) for t,s in zip((int,float,bool,str),input.split())]
print (a, b, c, d)

如果只是要切割單純被空格分開的每一個item,那麼上面的做法更快也更單純,若是牽扯到的pattern更為複雜,依然可以搭配正規表示式:

import re
input = '1:3.0 false,hello'
(a, b, c, d) = [t(s) for t,s in zip((int,float,bool,str),re.search('^(\d+):([\d.]+) (\w+),(\w+)$',input).groups())]
print (a, b, c, d)

results for ""

    No results matching ""