tomyamaのブログ

日記・雑記。

C言語 POSIX 正規表現(regex)

C言語で使える正規表現ライブラリのサンプルプログラムです。

 

 

 

  • regex [POSIX.1-2001準拠]
  • ヘッダーファイル
    • #include <sys/types.h>
      #include <regex.h>
  • 構造体
    • typedef struct{
        regoff_t  rm_so;      /* 次の最大マッチング部分の開始オフセット位置 */
        regoff_t  rm_eo;      /* 終了オフセット位置 */
      }regmatch_t;

  • 関数プロトタイプ一覧
    • int    regcomp( regex_t *preg, const char *regex, int cflags );
      int    regexec( const  regex_t  *preg, const char *string, size_t nmatch, regmatch_t pmatch, int eflags );
      size_t regerror( int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size );
      void   regfree( regex_t *preg );


目次


ソースコード

[regex.c]

#include <stdio.h>
#include <stdlib.h>         /* exit() */
#include <string.h>         /* strncpy() */
#include <sys/types.h>
#include <regex.h>          /* POSIX:                                      */
                            /* regcomp(), regexec(), regfree(), regerror() */


#define MY_NMATCH   3


const char      STRINGS = "123  4567.89  192.168.1.1  090-9876-5432" ;

int main( int argc, char *argv[] )
{

    char        buff[1024];
    regex_t     re;
    int         streg;
    regmatch_t  reg_match1, reg_match2[ MY_NMATCH ];
    int         offset, count;
    int         copy_sz, match_sz, buff_sz;
    int         idx, iRetRegexec;

    /*--- 引数チェック ---*/
    if( argc != 2 ){
        fprintf( stderr, "usage: %s <Regular_Expression>\n", argv[0] );
        exit( 1 );
    }

    /*--- 正規表現コンパイル ---*/
    if( ( streg = regcomp( &re, argv[1], REG_EXTENDED | REG_NEWLINE ) ) ){
        regerror( streg, &re, buff, sizeof buff / sizeof( char ) );
        fprintf( stderr, buff );
        exit( 1 );
    }

    buff_sz  = ( ( sizeof buff ) / ( sizeof buff[0] ) ) - 1;

    printf( "*** Global match ***\n" );
    printf( "           0         1         2         3\n" );
    printf( "           0123456789012345678901234567890123456789\n" );
    printf( "STRING = ``%s''\n", STRINGS );

    /*--- 正規表現で検索実行 ---*/
    for(   offset=0, count=0
           ;
           REG_NOMATCH != regexec(  /* グローバルマッチ */
               &re,                 /* preg   : コンパイルされた正規表現   */
               STRINGS + offset,    /* string : 検索対象文字列             */
               ( sizeof reg_match1 ) / sizeof( regmatch_t ),
                                    /* nmatch :                            */
               &reg_match1,         /* pmatch : マッチした位置の格納用配列 */
               0                    /* eflags : マッチング動作フラグ       */
           )
           ;
           offset += reg_match1.rm_eo, count++
    ){
        /*--- マッチした文字列をバッファにコピーする ---*/
        match_sz = ( reg_match1.rm_eo - reg_match1.rm_so );
        copy_sz = ( match_sz < buff_sz ) ? match_sz : buff_sz;  /* 最小値を選択 */
        strncpy( buff, ( STRINGS + offset + reg_match1.rm_so ), copy_sz );
        buff[ copy_sz ] = '\0';                                 /* 終端文字を挿入しておく */

        /*--- 検索結果としてレポート ---*/
        printf( "match: beg = %2ld, end = %2ld, ``%s''\n",
            ( long )( offset + reg_match1.rm_so ), ( long )( offset + reg_match1.rm_eo ), buff );
    }

    /*--- レポート ---*/
    if(count)   printf( "match_count = `%d'\n\n", count );
    else        printf( "no match\n\n" );


    printf( "*** Get up to %d backreferences ***\n", MY_NMATCH );
    printf( "           0         1         2         3\n" );
    printf( "           0123456789012345678901234567890123456789\n" );
    printf( "STRING = ``%s''\n", STRINGS );

    /*--- 正規表現で検索実行 ---*/
    iRetRegexec = regexec( &re, STRINGS, MY_NMATCH, reg_match2, 0 );
    if( iRetRegexec != REG_NOMATCH ){
        for( idx=0; idx<MY_NMATCH; idx++ ){
            /*--- マッチした文字列をバッファにコピーする ---*/
            match_sz = ( reg_match2[ idx ].rm_eo - reg_match2[ idx ].rm_so );
            copy_sz = ( match_sz < buff_sz ) ? match_sz : buff_sz;  /* 最小値を選択 */
            strncpy( buff, ( STRINGS + reg_match2[ idx ].rm_so ), copy_sz );
            buff[ copy_sz ] = '\0';                                 /* 終端文字を挿入しておく */

            /*--- 検索結果としてレポート ---*/
            printf( "match: beg = %2ld, end = %2ld, ``%s''\n",
                ( long )( reg_match2[ idx ].rm_so ), ( long )( reg_match2[ idx ].rm_eo ), buff );
        }
    }


    /*--- コンパイルした正規表現を解放 ---*/
    regfree( &re );


    return 0;
}

 

実行例

Cygwin 3.3.6-1, gcc (GCC) 11.3.0

$ gcc -Wall -O2 -o regex_cygwin regex.c
$
$ ./regex_cygwin.exe '[0-9]{1,3}'
*** Global match ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg =  0, end =  3, ``123''
match: beg =  5, end =  8, ``456''
match: beg =  8, end =  9, ``7''
match: beg = 10, end = 12, ``89''
match: beg = 14, end = 17, ``192''
match: beg = 18, end = 21, ``168''
match: beg = 22, end = 23, ``1''
match: beg = 24, end = 25, ``1''
match: beg = 27, end = 30, ``090''
match: beg = 31, end = 34, ``987''
match: beg = 34, end = 35, ``6''
match: beg = 36, end = 39, ``543''
match: beg = 39, end = 40, ``2''
match_count = `13'

*** Get up to 3 backreferences ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg =  0, end =  3, ``123''
match: beg = -1, end = -1, ``''
match: beg = -1, end = -1, ``''
$
$ ## 「456」の次は「7」ではなく「567」とマッチさせたいかもしれない。
$ ## その場合は、サンプルコードのoffsetの加算部分を修正すれば良い。
$
$ ## 下記の正規表現では後方参照を使っている。
$ ./regex_cygwin.exe '([0-9]{2,3})-([0-9]{4}-[0-9]{4})'
*** Global match ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg = 27, end = 40, ``090-9876-5432''
match_count = `1'

*** Get up to 3 backreferences ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg = 27, end = 40, ``090-9876-5432''
match: beg = 27, end = 30, ``090''
match: beg = 31, end = 40, ``9876-5432''
$
$ ./regex_cygwin.exe '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}'
*** Global match ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg = 14, end = 25, ``192.168.1.1''
match_count = `1'

*** Get up to 3 backreferences ***
           0         1         2         3
           0123456789012345678901234567890123456789
STRING = ``123  4567.89  192.168.1.1  090-9876-5432''
match: beg = 14, end = 25, ``192.168.1.1''
match: beg = -1, end = -1, ``''
match: beg = -1, end = -1, ``''
$