C語言中的正則表達式使用示例詳解

正則表達式是使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。本文通過示例代碼給大家介紹了C語言中的正則表達式使用,感興趣的朋友跟隨小編一起看看吧

正則表達式,又稱正規表示法、常規表示法(英語:Regular Expression,在代碼中常簡寫爲regex、regexp或RE)。正則表達式是使用單個字符串來描述、匹配一系列符合某個句法規則的字符串。

在c語言中,用regcomp、regexec、regfree 和regerror處理正則表達式。處理正則表達式分三步:

  1. 編譯正則表達式,regcomp;
  2. 匹配正則表達式,regexec;
  3. 釋放正則表達式,regfree。

函數原型

/*
函數說明:Regcomp將正則表達式字符串regex編譯成regex_t的形式,後續regexec以此進行搜索。
參數說明:
  Preg:一個regex_t結構體指針。
  Regex:正則表達式字符串。
  Cflags:是下邊四個值或者是他們的或(|)運算。
    REG_EXTENDED:使用POSIX擴展正則表達式語法解釋的正則表達式。如果沒有設置,基本POSIX正則表達式語法。
    REG_ICASE:忽略字母的大小寫。
    REG_NOSUB:不存儲匹配的結果。
    REG_NEWLINE:對換行符進行“特殊照顧”,後邊詳細說明。
返回值:
  0:表示成功編譯;
  非0:表示編譯失敗,用regerror查看失敗信息
*/
int regcomp(regex_t *preg, const char *regex, int cflags);
/*
函數說明: Regexec用來匹配正則文本。
參數說明:
  Preg:由regcomp編譯好的regex_t結構體指針,
  String:要進行正則匹配的字符串。
  Nmatch:regmatch_t結構體數組的大小
  Pmatch:regmatch_t結構體數組。用來保存匹配結果的子串位置。
  regmatch_t結構體定義如下
    typedef struct {
      regoff_t rm_so;
      regoff_t rm_eo;
    } regmatch_t;
    rm_so,它的值如果不爲-1,表示匹配的最大子串在字符串中的起始偏移量,rm_eo,表示匹配的最大字串在字符串的結束偏移量。
  Eflags: REG_NOTBOL和REG_NOTEOL爲兩個值之一或二者的或(|)運算,稍後會介紹。
返回值:
  0:表示成功編譯;
  非0:表示編譯失敗,用regerror查看失敗信息
*/
int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
/*
函數說明:用來釋放regcomp編譯好的內置變量。
參數說明:
  Preg:由regcomp編譯好的regex_t結構體指針。
*/
void regfree(regex_t *preg);
/*
函數說明:Regcomp,regexec出錯時,會返回error code並且爲非0,此時就可以用regerror得到錯誤信息。
參數說明:
  Errcode:Regcomp,regexec出錯時的返回值
  Preg:經過Regcomp編譯的regex_t結構體指針。
  Errbuf:錯誤信息放置的位置。
  errbuf_size:錯誤信息buff的大小。
*/
size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);

示例一

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "Hello World";
  char *reg_str = "H.*";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "%s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 0, NULL, 0);
  if (ret)
  {
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "%s\n", ebuff);
    goto end;
  }  
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "result is:\n%s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

編譯,輸出結果:

[root@zxy regex]# ./test
result is:
Success

匹配成功。

示例二

如果我想保留匹配的結果怎麼操作?那就得用到 regmatch_t 結構體了。重新改寫上邊代碼,這時就不能用REG_NOSUB選項了,代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  regmatch_t rm[5];
  char *part_str = NULL;
  cflags = REG_EXTENDED | REG_ICASE;
  char *test_str = "Hello World";
  char *reg_str = "e(.*)o";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "%s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 5, rm, 0); 
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "%s\n", ebuff);
    goto end;
  }
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "result is:\n%s\n\n", ebuff);
  for (i=0; i<5; i++)
  {
    if (rm[i].rm_so > -1)
    {
      part_str = strndup(test_str+rm[i].rm_so, rm[i].rm_eo-rm[i].rm_so);
      fprintf(stderr, "%s\n", part_str);
      free(part_str);
      part_str = NULL;
    }
  }
end:
  regfree(&reg);
  return 0;
}

編譯,輸出結果:

[root@zxy regex]# ./test
result is:
Success
ello Wo
llo W

  咦??????我明明只要一個匹配結果,爲什麼會打印兩個出來呢???????
  原來regmatch_t數組的第一個元素是有特殊意義的:它是用來保存整個正則表達式能匹配的最大子串的起始和結束偏移量。所以我們在設置regmatch_t數組個數的時候一定要記住,它的個數是最大保留結果數+1。

REG_NEWLINE、REG_NOTBOL和REG_NOTEOL

好了,基本的正則運用到此爲止了,現在要開始講講REG_NEWLINE、REG_NOTBOL和REG_NOTEOL。很多人對這三個參數有所迷惑。我也是,昨天有人問問題,就把自己錯誤的理解告訴了別人,然後被大神一頓鄙視。我一直認爲如果想用^和$這兩個匹配模式一定要用到REG_NEWLINE這個參數,其實不然。

REG_NEWLINE

首先看下man page對REG_NEWLINE的說明:

REG_NEWLINE
  Match-any-character operators don't match a newline.
  A non-matching list ([^...]) not containing a newline does not match a newline.
  Match-beginning-of-line operator (^) matches the empty string immediately after a newline, regardless of whether eflags, the execution flags of regexec(), contains REG_NOTBOL.
  Match-end-of-line operator ($) matches the empty string immediately before a newline, regardless of whether eflags contains REG_NOTEOL.

我英文不好,google翻譯之。。

REG_NEWLINE

  1.匹配任何字符的運算符(比如.)不匹配換行('\n');
  2.非匹配列表([^...])不包含一個換行符不匹配一個換行符;
  3.匹配開始運算符(^)遇到空字符串立即換行,不論在執行regexec()時,eflags是否設置了REG_NOTBOL;
  4.匹配結束運算符($)遇到空字符串立即換行,不論在執行regexec()時,eflags是否設置了REG_NOTEOL;

不明白說的是什麼,程序測之。。

第一個問題

代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "Hello World\n";
  char *reg_str = "Hello World.";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "1. %s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 0, NULL, 0); 
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "2. %s\n", ebuff);
  cflags |= REG_NEWLINE;
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "3. %s\n", ebuff);
    goto end;
  }
  ret = regexec(&reg, test_str, 0, NULL, 0);
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "4. %s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

  編譯,運行結果如下:

[root@zxy regex]# ./test
2. Success
4. No match

  結果很明顯:沒有加入REG_NEWLINE的匹配成功,加入的匹配不成功。就是說不加入REG_NEWLINE,任意匹配字符(.)包含'n',加入則不包含'n'。

第二個問題

代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "Hello\nWorld";
  char *reg_str = "Hello[^ ]";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "1. %s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 0, NULL, 0); 
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "2. %s\n", ebuff);
  cflags |= REG_NEWLINE;
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "3. %s\n", ebuff);
    goto end;
  }
  ret = regexec(&reg, test_str, 0, NULL, 0);
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "4. %s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

編譯,運行結果如下:

[root@zxy regex]# ./test
2. Success
4. No match

  結果說明:不加入REG_NEWLINE,在一個不包含'n'的非列表中,'n'是不被認作空白符,加入則'n'是被認作空白符。

第三個問題

代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "\nHello World";
  char *reg_str = "^Hello";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "1. %s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 0, NULL, 0); 
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "2. %s\n", ebuff);
  cflags |= REG_NEWLINE;
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "3. %s\n", ebuff);
    goto end;
  }
  ret = regexec(&reg, test_str, 0, NULL, 0);
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "4. %s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

編譯,運行結果如下:

[root@zxy regex]# ./test
2. No match
4. Success

  結果說明:不加入REG_NEWLINE,'^'是不忽略'n'的,加入REG_NEWLINE,'^'是忽略'n'的。也就是說:不加入REG_NEWLINE,以'n'開頭的字符串是不能用'^'匹配,加入REG_NEWLINE,以'n'開頭的字符串是可以用'^'匹配。

第四個問題

代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "Hello World\n";
  char *reg_str = "d$";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "1. %s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str, 0, NULL, 0); 
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "2. %s\n", ebuff);
  cflags |= REG_NEWLINE;
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "3. %s\n", ebuff);
    goto end;
  }
  ret = regexec(&reg, test_str, 0, NULL, 0);
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "4. %s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

編譯,運行結果如下:

[root@zxy regex]# ./test
2. No match
4. Success

  結果說明:不加入REG_NEWLINE,'&dollar;'是不忽略'n'的,加入REG_NEWLINE,'&dollar;'是忽略'n'的。也就是說:不加入REG_NEWLINE,以'n'結尾的字符串是不能用'​&dollar;'匹配,加入REG_NEWLINE,以'n'開頭的字符串是可以用'​&dollar;'匹配。

REG_NEWLINE總結

好,REG_NEWLINE選項測試到此結束。總結下:

  對於REG_NEWLINE選項,1.使用任意匹配符(.)時,任意匹配符不會包含'n';2.對於一個不含有'n'的非列表,會把'n'認作空白符。3.對於以'n'開頭或結尾的字符串,會忽略'n'。使'^'和'$'可以使用。

REG_NOTBOL和REG_NOTEOL

現在開始說下REG_NOTBOL和REG_NOTEOL,首先看下man page對這兩選項的說明:

REG_NOTBOL
  The match-beginning-of-line operator always fails to match (but see the compilation flag REG_NEWLINE above) This flag may be used when different portions of a string are passed to regexec() and the beginning of the string should not be interpreted as the beginning of the line.
REG_NOTEOL
  The match-end-of-line operator always fails to match (but see the compilation flag REG_NEWLINE above)
繼續googling。

REG_NOTBOL
  匹配開始操作符(^)會經常匹配失敗(但是要考慮REG_NEWLINE),這個標誌被用在當一個字符串的不同位置被傳入到regexec()時,這個位置不應該被解釋爲該整個字符串的開始位置。
REG_NOTEOL
  匹配結束操作符($)會經常失敗(但是要考慮REG_NEWLINE)。(這個標誌被用在當一個字符串的不同位置被傳入到regexec()時,即使滿足匹配結束作符,也不應該被解釋爲以某字符(串)爲結束的)。

  好吧,繼續測試,第一個問題代碼如下:

#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <regex.h>
int main (void)
{
  int i;
  char ebuff[256];
  int ret;
  int cflags;
  regex_t reg;
  cflags = REG_EXTENDED | REG_ICASE | REG_NOSUB;
  char *test_str = "Hello World\n";
  char *reg_str = "^e";
  ret = regcomp(&reg, reg_str, cflags);
  if (ret)
  {  
    regerror(ret, &reg, ebuff, 256);
    fprintf(stderr, "1. %s\n", ebuff);
    goto end;
  }  
  ret = regexec(&reg, test_str+1, 0, NULL, 0); 
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "2. %s\n", ebuff);
  ret = regexec(&reg, test_str+1, 0, NULL, REG_NOTBOL);
  regerror(ret, &reg, ebuff, 256);
  fprintf(stderr, "4. %s\n", ebuff);
end:
  regfree(&reg);
  return 0;
}

編譯,運行結果如下:

[root@zxy regex]# ./test
2. Success
4. No match

結果說明:不加入REG_NOTBOL,一個字符串的不同位置是可以用'^'進行匹配,加入REG_NOTBOL,則不能進行匹配。

第二個問題,我實在理解不了了,網上介紹的全是沒有經過驗證的。。。。。。

總結

以上所述是小編給大家介紹的C語言中的正則表達式使用示例詳解,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對神馬文庫網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請註明出處,謝謝!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章