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语言中的正则表达式使用示例详解,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

(0)

相关推荐

  • C语言正则表达式详解 regcomp() regexec() regfree()用法详解

    标准的C和C++都不支持正则表达式,但有一些函数库可以辅助C/C++程序员完成这一功能,其中最著名的当数Philip Hazel的Perl-Compatible Regular Expression库,许多Linux发行版本都带有这个函数库. C语言处理正则表达式常用的函数有regcomp().regexec().regfree()和regerror(),一般分为三个步骤,如下所示: C语言中使用正则表达式一般分为三步: 编译正则表达式 regcomp() 匹配正则表达式 regexec() 释

  • c语言 sscanf,scanf,fscanf正则表达式用法

    每种语言都对正则表达式有着不同程度的支持,在C语言中,有输入功能的这三个函数对正则表达式的支持并不强大,但是我们还是有必要了解一下. 首先来看看他们的原型: #include <stdio.h> int scanf(const char *format, ...); int fscanf(FILE *stream, const char *format, ...); int sscanf(const char *str, const char *format, ...); 均可以接受变参,ss

  • C语言正则表达式操作示例

    本文实例讲述了C语言正则表达式操作.分享给大家供大家参考,具体如下: #include <stdio.h> #include <sys/types.h> #include <regex.h> int main(int argc,char**argv) { int status; int i; int cflags = REG_EXTENDED; regmatch_t pmatch[1]; const size_t nmatch =1 ; regex_t reg; con

  • C语言中的正则表达式使用示例详解

    正则表达式,又称正规表示法.常规表示法(英语:Regular Expression,在代码中常简写为regex.regexp或RE).正则表达式是使用单个字符串来描述.匹配一系列符合某个句法规则的字符串. 在c语言中,用regcomp.regexec.regfree 和regerror处理正则表达式.处理正则表达式分三步: 编译正则表达式,regcomp: 匹配正则表达式,regexec: 释放正则表达式,regfree. 函数原型 /* 函数说明:Regcomp将正则表达式字符串regex编译

  • Go语言中序列化与反序列化示例详解

    目录 前言 序列化 array.slice.map.struct对象 序列化的接口 反序列化 slice.map.struct反序列化 总结 前言 Go语言的序列化与反序列化在工作中十分常用,在Go语言中提供了相关的解析方法去解析JSON,操作也比较简单 序列化 // 数据序列化 func Serialize(v interface{})([]byte, error) // fix参数用于添加前缀 //idt参数用于指定你想要缩进的方式 func serialization (v interfa

  • C语言中二级指针的实例详解

    C语言中二级指针的实例详解 用图说明 示例代码: #include <stdio.h> int main(int argc, const char * argv[]) { // int a = 5; int *p1 = &a; //-打印地址-----地址相同--------------- printf("&a = %p\n", &a);// printf("p1 = %p\n", p1);// int **p2 = &p

  • Java语言中的内存泄露代码详解

    Java的一个重要特性就是通过垃圾收集器(GC)自动管理内存的回收,而不需要程序员自己来释放内存.理论上Java中所有不会再被利用的对象所占用的内存,都可以被GC回收,但是Java也存在内存泄露,但它的表现与C++不同. JAVA中的内存管理 要了解Java中的内存泄露,首先就得知道Java中的内存是如何管理的. 在Java程序中,我们通常使用new为对象分配内存,而这些内存空间都在堆(Heap)上. 下面看一个示例: public class Simple { public static vo

  • 正则表达式使用示例详解

    正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为"元字符"). 正则表达式使用单个字符串来描述.匹配一系列匹配某个句法规则的字符串. 下面通过实例代码介绍下正则表达式使用 //一个数据类型,记录文本规则,一些事先定义好的一些特殊字符,对字符串的过滤逻辑 //表单验证账号长度,字母或数字组合,高级搜索 //特点,不仅js有:极简的方式操作字符串:灵活,功能强大 // 正则表达式大全 var patt1=new

  • VSCode各语言运行环境配置方法示例详解

    系统环境变量的配置 如:将F:\mingw64\bin添加到系统环境变量Path中 VSCode软件语言json配置C语言 创建个.vscode文件夹,文件夹内创建以下两个文件 launch.json 文件配置 { "version": "0.2.0", "configurations": [ { "name": "(gdb) Launch", "type": "cppdbg&

  • c语言左移和右移的示例详解

    逻辑移位,简单理解就是物理上按位进行的左右移动,两头用0进行补充,不关心数值的符号问题. 算术移位,同样也是物理上按位进行的左右移动,两头用0进行补充,但必须确保符号位不改变. 算术移位指令 算术移位指令有:算术左移SAL(ShiftAlgebraic Left)和算术右移SAR(ShiftAlgebraic Right).算术移位指令的功能描述如下: (1)算术左移SAL把目的操作数的低位向高位移,空出的低位补0: (2)算术右移SAR把目的操作数的高位向低位移,空出的高位用最高位(符号位)填

  • R语言时间序列TAR阈值自回归模型示例详解

    为了方便起见,这些模型通常简称为TAR模型.这些模型捕获了线性时间序列模型无法捕获的行为,例如周期,幅度相关的频率和跳跃现象.Tong和Lim(1980)使用阈值模型表明,该模型能够发现黑子数据出现的不对称周期性行为. 一阶TAR模型的示例: σ是噪声标准偏差,Yt-1是阈值变量,r是阈值参数, {et}是具有零均值和单位方差的iid随机变量序列. 每个线性子模型都称为一个机制.上面是两个机制的模型. 考虑以下简单的一阶TAR模型: #低机制参数 i1 = 0.3 p1 = 0.5 s1 = 1

  • Go语言基础单元测试与性能测试示例详解

    目录 概述 单元测试 代码说明如下 问题 注意 性能测试 基本使用 自定义测试时间 概述 测试不是Go语言独有的,其实在很多语言都有测试,例如:Go.Java.Python- 要想成为一名合格的大牛,这是程序员必须具备的一项技能,特别是一些大公司,这是加分的一项,主要有如下优点: 代码可以随时测试,保证代码不会产生错误 写出更加高效的代码 testing文档 Testing_flags文档 单元测试 格式:func TestXXX(t *testing.T) //add.go package c

  • Go语言基础结构体用法及示例详解

    目录 概述 语法 结构体定义的三种形式 第一种[基本的实例化] 第二种[指针类型的结构体] 第三种[取结构体的地址实例化,通过&的操作] 初始化结构体 键值对初始化结构体 值列表填充结构体 匿名结构体 访问结构体成员 结构体作为函数参数 结构体指针 添加结构体方法 总结 示例 概述 结构体是由一系列具有相同类型或不同类型的数据构成的数据集合 语法 定义结构体[标识自定义结构体的名称,在同一个包内不能重复] type 结构名 struct { 字段1: 字段1的值, 字段2: 字段2的值, ...

随机推荐