使用GNU C正規表示式

2021-06-16 07:43:18 字數 4034 閱讀 3802

前不久是我第二次使用c語言的正規表示式庫。用的時候竟發現以前用過一次的全忘了,沒辦法,只有從新去分析regex.h這個標頭檔案的內容,猜著它是怎麼工作的。

看來很有必要記下來,免得第三次用的時候再去猜一遍。

「regex.h」是linux系統預設安裝的乙個標頭檔案,我們就是用裡面的函式來使用正規表示式的功能。

先看看它的大體流程吧:

定義模板這個不說了。設定語法風格,就是選擇你要使用那一種正規表示式規則。各種不同的程式裡面,對正規表示式的元字元以及一些規則的定義是不一樣的。在這乙個步驟,可以設定一種最適合,或者自己最熟悉的風格。編譯模板,可能一部分原因是為了效率,這個不想深究。匹配,這個不用說了吧。

假設你要處理的是這樣一種字串:一串11位數字的**號碼,彼此用「;」分隔,在每個號碼後面可能會有「,b」表示黑名單裡的「,w」表示白名單,或者沒有(如:「18753432222,b;19885812345,w;18712345678;18787654321,w」)。看如下**:

#include #include #include #include static int pat_compiled = 0;

const static char* pat_number = "^[0-9](,[bw])?(;[0-9](,[bw])?)*$;

static regex_t pat_buf[1];

int print_number(char* src)

pat_compiled = 1;

} memset(match_rlt,0,sizeof(struct re_registers));

match_ret = re_match(pat_buf,src,strlen(src),0,match_rlt);

if (-2 == match_ret)

if (-1 == match_ret)

for (i = 0; i < match_rlt->num_regs; ++i)

tmp_p = strndup(src + match_rlt->start[i],match_rlt->end[i] - match_rlt->start[i]);

printf("start: %d end: %d\n",match_rlt->start[i],match_rlt->end[i]);

printf("%s\n",tmp_p);

free(tmp_p);

} return 0;

}int main(int argc, char **argv)

程式輸出:

start: 0 end: 53

18753432222,b;19885812345,w;18712345678;18787654321,w

start: 11 end: 13

,bstart: 39 end: 53

;18787654321,w

start: 51 end: 53

,w

re_set_syntax(re_syntax_posix_egrep);
首先,程式選擇的是posix egrep的風格,也就是grep程式加了-e選項時候使用的風格。選這個的原因,除了熟悉意外,最重要的就是好測試表示式的正確性。

// 使用之前,先對模板進行編譯

if (null != (compile_ret = re_compile_pattern(pat_number,strlen(pat_number),pat_buf)))

編譯的返回值,如果為null則表示成功,否則為乙個指向了描述失敗原因字串的指標。

match_ret = re_match(pat_buf,src,strlen(src),0,match_rlt);

if (-2 == match_ret)

if (-1 == match_ret)

匹配的函式,如果返回-2表示內部錯誤,返回-1表示字串不匹配,否則返回第乙個匹配的字元索引。匹配結果放在match_rlt結構體中。

我們看看這個結構體的定義:

struct re_registers

;

這裡得解釋一下「匹配的數量」。匹配數不是指元字串中匹配整個正則是模板的有幾個,而是指的捕獲的字串數量。

"^[0-9](,[bw])?(;[0-9](,[bw])?)*$;
三個括號,為什麼會有4個捕獲呢?請看第一組捕獲,它把整個正則是看做乙個捕獲。所以一旦匹配,num_regs的值一點是括號對數加1.

那麼問題又來了,源字串是這樣的:

"18753432222,b;19885812345,w;18712345678;18787654321,w"
根據

(;[0-9](,[bw])?)*
那麼括號部分一共重複了3次,為什麼才算了一次呢?

這個問題還沒有想通,可能是為了使用者程式更穩定的考慮。因為它根據模板中可見的括號數來捕獲,使得捕獲數在程式設計時就確定了,這樣免去了很多可能由於處理不當可能導致的錯誤。不過功能上,就有所打折了。

不過仔細看re_match函式的引數設定,它給我提供了另外乙個方法來實現捕獲每乙個號碼。

/* search in the string string (with length length) for the pattern

compiled into buffer. start searching at position start, for range

characters. return the starting position of the match, -1 for no

match, or -2 for an internal error. also return register

information in regs (if regs and buffer->no_sub are nonzero). */

extern int re_search (struct re_pattern_buffer *__buffer, const char *__string,

int __length, int __start, int __range,

struct re_registers *__regs);

/* like `re_search', but return how many characters in string the regexp

in buffer matched, starting at position start. */

extern int re_match (struct re_pattern_buffer *__buffer, const char *__string,

int __length, int __start, struct re_registers *__regs);

通過第四個引數__start,我們可以指定匹配開始的地方。我們只用再迴圈中每次從匹配的下乙個位置開始匹配,不就每次都取出新的號碼了嗎?

經測試,不管用,肯定是用的方法不對^_^,不過一件事可以通過多種不同的方法實現。

我們每次把輸入的string往後推乙個號碼,**如下:

#include #include #include #include static int pat_compiled = 0;

const static char* pat_number = "^([0-9])(,[bw])?(;[0-9](,[bw])?)*quot;;

static regex_t pat_buf[1];

int print_number(char* src)

pat_compiled = 1;

} while (1)

if (-1 == match_ret)

if (-1 != match_rlt->start[1])

else

} return 0;

}int main(int argc, char **argv)

使用正規表示式

如果原來沒有使用過正規表示式,那麼可能對這個術語和概念會不太熟悉。不過,它們並不是您想象的那麼新奇。請回想一下在硬碟上是如何查詢檔案的。您肯定會使用 和 字元來幫助查詢您正尋找的檔案。字元匹配檔名中的單個字元,而 則匹配乙個或多個字元。乙個如 data?dat 的模式可以找到下述檔案 data1.d...

使用正規表示式

本文節選自 並有稍微修正。使用正規表示式 您可以使用正規表示式做很多事情。在以下的列表中,您可以找到一些最普通 最常用的正規表示式的例子。表示文字串必須在一行的開頭。所以,當查詢行的開頭只為 hosts 的行,可以使用命令 grep ls hosts 代表了一行的結尾。所以,當查詢行的結尾只為 ho...

正規表示式使用

1 靜態match方法 使用靜態match方法,可以得到源中第乙個匹配模式的連續子串。2 靜態的matches方法 這個方法的過載形式同靜態的match方法,返回乙個matchcollection,表示輸入中,匹配模式的匹配的集合。3 靜態的ismatch方法 此方法返回乙個bool,過載形式同靜態...