専門家はあなたにどのようにC言語の中で巧みに正規表現を使うことを教えます


Linuxのsed、awk、grep、viに詳しいユーザーは、正規表現という概念に慣れているに違いありません.文字列を処理する際の複雑さを極めて簡略化できるため、多くのLinuxユーティリティで応用されている.正規表現はPerl、Python、Bashなどのスクリプト言語の特許にすぎないとは思わないでください.C言語プログラマーとして、ユーザーも自分のプログラムで正規表現を運用することができます.
標準的なCとC++は正規表現をサポートしていませんが、C/C++プログラマーがこの機能を完了するのを支援する関数ライブラリがあります.最も有名な当数Philip HazelのPerl-Compatible Regular Expressionライブラリで、多くのLinuxリリースバージョンにこの関数ライブラリがあります.
正規表現のコンパイル
効率を向上させるには、文字列を正規表現と比較する前にregcomp()関数でコンパイルし、regex_に変換します.t構造:

       int regcomp(regex_t *preg, const char *regex,

int cflags);

パラメータregexは、コンパイルされる正規表現を表す文字列です.パラメータpregはregexとして宣言されたものを指します.tのデータ構造は、コンパイル結果を保存するために使用される.パラメータcflagsは、正規表現がどのように処理されるかの詳細を決定します.
関数regcomp()が正常に実行され、コンパイル結果がpregに正しく埋め込まれた場合、関数は0を返し、他の戻り結果は何らかのエラーが発生したことを表します.
正規表現の一致
regcomp()関数で正規表現のコンパイルに成功すると、regexec()関数を呼び出してモードマッチングを完了できます.

       int regexec(const regex_t *preg, 

const char *string, size_t nmatch,

regmatch_t pmatch[], int eflags); 

typedef struct { 

regoff_t rm_so; 

regoff_t rm_eo; 

} regmatch_t;

パラメータpregはコンパイルされた正規表現を指し、パラメータstringはマッチングする文字列であり、パラメータnmatchとpmatchはマッチング結果を呼び出しプログラムに返すために使用され、最後のパラメータeflagsはマッチングの詳細を決定する.
関数regexec()を呼び出してモードマッチングを行う過程で、文字列stringで所定の正規表現と一致する箇所が複数ある可能性があります.パラメータpmatchはこれらのマッチング位置を保存するために使用され、パラメータnmatchは関数regexec()に、最大で複数のマッチング結果をpmatch配列に埋め込むことができることを示します.regexec()関数が正常に戻るとstring+pmatch[0]から.rm_soからstring+pmatch[0].rm_eoはstring+pmatch[1]から最初に一致する文字列である.rm_soからstring+pmatch[1].rm_eoは、2番目に一致する文字列であり、このように推定されます.
正規表現の解放
コンパイルされた正規表現が不要になった場合は、メモリ漏洩が発生しないように関数regfree()を呼び出して解放する必要があります.

       void regfree(regex_t *preg);

関数regfree()は結果を返さず、regex_を指す1つだけを受信します.regcomp()関数を呼び出して得られたコンパイル結果であるtデータ型のポインタ.
プログラム内で同じregex_に対してt構造は複数回regcomp()関数を呼び出し、POSIX規格では毎回regfree()関数を呼び出して解放する必要があるかどうかは規定されていないが、regcomp()関数を呼び出して正規表現をコンパイルするたびにregfree()関数を呼び出し、占有する記憶空間をできるだけ早く解放することを提案する.
エラーメッセージのレポート
呼び出し関数regcomp()またはregexec()が0以外の戻り値である場合、正規表現の処理中にエラーが発生したことを示し、関数regerror()を呼び出すことで詳細なエラー情報を得ることができます.

       size_t regerror(int errcode, 

const regex_t *preg, char *errbuf,

size_t errbuf_size);

パラメータerrcodeは関数regcomp()またはregexec()からのエラーコードであり、パラメータpregは関数regcomp()から得られたコンパイル結果であり、フォーマットメッセージに必要なコンテキストをregerror()関数に提供することを目的とする.関数regerror()を実行すると、パラメータerrbuf_に従います.sizeが示す最大バイト数は、errbufバッファにフォーマットされたエラー情報を入力し、エラー情報の長さを返します.
正規表現の適用
最後に、C言語プログラムで正規表現を処理する方法について具体的な例を示します.

       #include <stdio.h> 

#include <sys/types.h> 

#include <regex.h> 



/*        */ 

static char* substr(const char*str,

unsigned start, unsigned end) 

{ 

unsigned n = end - start; 

static char stbuf[256]; 

strncpy(stbuf, str + start, n); 

stbuf[n] = 0; 

return stbuf; 

} 

/*     */ 

int main(int argc, char** argv) 

{ 

char * pattern; 

int x, z, lno = 0, cflags = 0; 

char ebuf[128], lbuf[256]; 

regex_t reg; 

regmatch_t pm[10]; 

const size_t nmatch = 10; 

/*        */ 

pattern = argv[1]; 

z = regcomp(?, pattern, cflags); 

if (z != 0){ 

regerror(z, ?, ebuf, sizeof(ebuf)); 

fprintf(stderr, "%s: pattern '%s' /n",

ebuf, pattern); 

return 1; 

} 

/*           */ 

while(fgets(lbuf, sizeof(lbuf), stdin))

{ 

++lno; 

if ((z = strlen(lbuf)) > 0 && lbuf[z-1]

== '/n') 

lbuf[z - 1] = 0; 

/*                 */ 

z = regexec(?, lbuf, nmatch, pm, 0); 

if (z == REG_NOMATCH) continue; 

else if (z != 0) { 

regerror(z, ?, ebuf, sizeof(ebuf)); 

fprintf(stderr, "%s: regcom('%s')/n",

ebuf, lbuf); 

return 2; 

} 

/*        */ 

for (x = 0; x < nmatch && pm[x].rm_so != -1; ++ x)

{ 

if (!x) printf("%04d: %s/n", lno, lbuf); 

printf(" $%d='%s'/n", x, substr(lbuf, pm[x].rm_so,

pm[x].rm_eo)); 

} 

} 

/*         */ 

regfree(?); 

return 0; 

}

上記のプログラムは、コマンドラインから正規表現を取得し、標準入力から得られた各行のデータに適用し、一致結果を印刷する責任を負います.プログラムをコンパイルして実行するには、次のコマンドを実行します.

       # gcc regexp.c -o regexp 

# ./regexp 'regex[a-z]*' < regexp.c 

0003: #include <regex.h> 

$0='regex' 

0027: regex_t reg; 

$0='regex' 

0054: z = regexec(?, lbuf, nmatch, pm, 0); 

$0='regexec'

小結
複雑なデータ処理が必要なプログラムにとって、正規表現は非常に有用なツールに違いありません.本稿では,データ処理においてPerl言語と同様の柔軟性を得るために,C言語で正規表現を用いて文字列処理を簡略化する方法に重点を置いた.