linux C语言处理正则表达式

Linux下C语言处理正则表达式——regex.h

具体函数介绍

编译正则表达式函数

int regcomp(regex_t *preg, const char *regex, int cflags);

其中preg用于保存编译后的正则表达式,regex是我们写的正则表达式。cflags标志位后面再说。

先说说regex_t结构体:

对于这个结构体而言,我们只要记住,它是编译后的正则表达式,后面的匹配是用编译后的正则表达式,这样效率更高,而不是使用我们自己写的原始的正则表达式。此外,还要知道regex_t有一个成员re_nsub,它表示“子正则表达式的个数”。所谓“子正则表达式”就是圆括号里面的正则表达式。可能还是有点懵。没关系,慢慢来!我们使用正则表达式的一个主要目的是提取满足条件的部分。比如有个字符串username=阿星&sex=女,现在我们想提取用户名,也是就“阿星”,那么我们的正则表达式应该写成: username=([^&]*)&,也就是将匹配“阿星”的正则表达式放到圆括号中,作为整个表达式的一个子表达式。后面我们执行regexec函数后,就可以得到“阿星”(后面再讲)。

再来说说cflags:

cflags 的取值有:REG_EXTENDED、REG_ICASE、REG_NOSUB和REG_NEWLINE。这四个值可以单独使用,也可以用按位与联合使用。

其中:

REG_EXTENDED:

意思是,解释正则表达式时使用扩展的正则表达式语法。POSIX规范将正则表达式的实现方法分为了两种:基本正则表达式(BRE)和扩展正则表 达式(ERE)。

BRE和ERE到底有什么区别?其实仅仅是元字符的不同!在BRE方式中,只承认^ 、$、 . 、[ 、] 、*这些是元字符,所有其他的字符都被识别为文字字符。而ERE中,则添加了(、 ) 、{ 、} 、?、 + |、等元字符(及其相关功能)。grep命令默认支持BRE,要想支持ERE需要使用-E选项。

REG_ICASE:

如果编译正则表达式时使用了这个标志,那么在用regexec()函数进行匹配时,就忽略大小写。

REG_NOSUB:

如果使用了这个选项得到的编译后的正则表达式,在被后面的regexec()函数使用时,regexec()的nmatch参数和pmatch参数将会被忽略(后面再讲)

REG_NEWLINE:

一开始我对这个标志位的理解非常模糊,网上很多人解释的也不清楚。经过我的反复试验,终于明白了。

其实REG_NEWLINE的作用就两个:

1、         使^和$有效。

2、         绝对不匹配换行符。

相信大家也都看过Linux中的man page。对于REG_NEWLINE这个标志位的解释,在man page中用了四句话。

我们先来看后两句:

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.

这两句的意思其实就是,是^匹配一行的开始位置,$匹配一行的结束位置(如果没有使用REG_NEWLINE,这两个字符将被当做普通字符)。并且使REG_NOTBOL和REG_NOTEOL无效。

举两个例子:

有字符串:

username=xinger&sex=girl&age=22\r\nschool=BIT&husband=qinger\r\n&like=study&look=pretty\r\n

如果我们没有使用REG_NEWLINE标志,那么正则表达式^school=([^&]*)将不能匹配,因为这里^被解释成了普通字符,而不是一行的开始。

如果我们加上REG_NEWLINE标志,那么将匹配成school=BIT,此时^不再是普通字符,而是匹配一行的开始。

再比如正则表达式age=([^$]*),如果没有使用REG_NEWLINE,将匹配成:

age=22\r\nschool=BIT&husband=qinger\r\n&like=study&look=pretty\r\n

还是因为$被解释成了普通字符,

比如我们在原字符串中添加一个$,变成

username=xinger&sex=girl&age=22\r\nschool=$BIT&husband=$qinger\r\n&like=study&look=pretty\r\n

那么匹配结果变成了age=22\r\nschool=,原因依然是:把$当成了普通字符。

在来看前两句:

Match-any-character operators don‘t match a newline.

A  nonmatching  list ([^...])  not containing a newline does not match a newline.

这两句的意思说白了,就是保证不匹配换行符。比如第一句,意思是匹配任意字符的元字符也不匹配新的一行(好乱呀)。什么意思呢?就是比如说点(.)本来匹配所有的字符(注意,在POSIX中点匹配所有字符!!!和我们平时学的不一样。)但是如果使用了REG_NEWLINE标志,则不匹配换行符\r\n。

还是举个例子吧:

有字符串:

username=xinger&sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n

有正则表达式:sex=([^@]*),如果没有REG_NEWLINE标志,匹配结果是:

sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n

因为[^@]匹配所有不是@的字符。但是如果我们加上了REG_NEWLINE,那么匹配结果为:sex=girl&age=22,原因是REG_NEWLINE保证了绝不匹配换行符!!!其实就相当于[^@\r\n]不加REG_NEWLINE。

在比如,有正则表达式sex=(.*),我们前面提到过:点在POSIX中匹配任意字符(’\0’除外),所以点也匹配换行符,所以匹配结果为:

sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n

但是,如果我们使用了REG_NEWLINE,则保证不会匹配换行符,匹配结果就变成了:sex=girl&age=22。

最后说说返回值:

成功返回0,失败可以用regerror获取失败码。

用编译后的正则表达式进行匹配

int regexec(const regex_t *preg, const char *string, size_t nmatch,

                   regmatch_t pmatch[], int eflags);

regexec函数用上一步中编译好的正则表达式preg对string内容进行匹配,并将匹配结果以记录字节偏移量的形式保存在pmatch数组中。

首先看看regmatch_t结构体:

regmatch_t 是一个结构体数据类型,在regex.h中定义:

typedef struct

{

   regoff_t rm_so;

   regoff_t rm_eo;

} regmatch_t;

regexec函数将用匹配的子字符串的起止地址填充pmatch结构体,pmatch[0]对应的是整个正则表达式的匹配结果的起止地址;pmatch[i]则对应存储了第i个子匹配字符串的起止地址。(rm_so表示起始位置距离首地址的偏移量,rm_eo表示结束位置距离首地址的偏移量+1,如果rm_so为-1则表示该子表达式没有匹配)。nmatch表示pmatch结构体数组的元素的个数,它至少应该是子表达式的个数加1(因为0下标存储的是整个表达式的匹配结果)。

再来说说eflags:

首先一点,如果regcomp中使用了REG_NEWLINE变量,这个标志位是无效的!

这个标志位有两个取值:REG_NOTBOL和REG_NOTEOL,作用就是:如果设置了相应的标志位,那么含有^或$,而且含义是一行开始,或结束(比如^也可以解释成非),那么该正则表达式将永远不会匹配!!!。

最后说说返回值:

成功返回0,REG_NOMATCH表示失败。

将错误码转换成错误信息

size_t regerror(int errcode, const regex_t *preg, char *errbuf,

                       size_t errbuf_size);

该函数用于将regcomp或regexec返回的错误码转换成错误字符串信息。

参数errcode表示那两个函数返回的错误码,preg是regcomp编译后的正则表达式,errbuf用于存储错误信息字符串,errbuf_size是errbuf的大小。

释放regex_t结构体

void regfree(regex_t *preg);

regcomp函数会填写regex_t结构体的元素,这之中需要为某些元素开辟存储空间,而regfree函数就是释放这些空间的。

千万记得最后要调用regfree释放空间,否则会造成内存泄漏。

最后附上一个小例子:

#include <sys/types.h>
#include <regex.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

static int compile_regex(regex_t* compiled,const char* pattern)
{
    char errbuf[1024];
     int err;
    if(   (err=regcomp(compiled,pattern,REG_EXTENDED|REG_ICASE) ) !=0)
    {
         regerror(err,compiled,errbuf,sizeof(errbuf));
         printf("err:%s\n",errbuf);
        return -1;
     }
     return 0;

}

int main()
{

    const char *dest = "username=xinger&sex=girl&age=22\r\nschool=BIT&husband=$qinger\r\n&like=study&look=pretty\r\n";
    printf("原始数据为:\n%s\n",dest);
    const char *pattern = "school=([^&]*)";
    int err;
    char errbuf[1024];
    regex_t compiled;
    if((err = regcomp(&compiled,pattern,REG_EXTENDED|REG_ICASE)) != 0)
    {
         regerror(err,&compiled,errbuf,sizeof(errbuf));
         printf("err:%s\n",errbuf);
        return -1;
    }

    regmatch_t pmatch[3];
    err = regexec(&compiled,dest,3,pmatch,REG_NOTBOL);
    if(err != 0)
    {
        printf("未匹配成功!\n");
        return -1;
    }
    int i = 0;
    for(;i <= (int)compiled.re_nsub;i++)
    {
        if(pmatch[i].rm_so == -1)
            continue;
        int len = pmatch[i].rm_eo - pmatch[i].rm_so;
        char *value = (char *)malloc(len + 1);
        if(value == NULL)
            return -1;
        memset(value,0,len + 1);
        memcpy(value,dest + pmatch[i].rm_so,len);
        printf("提取的值为:%s\n",value);
        free(value);

    }
    regfree(&compiled);//切记最后要释放掉,否则会造成内存泄露

    return 0;
}

如果你觉得对你有用,就点个赞吧~~~

时间: 2024-08-24 22:59:27

linux C语言处理正则表达式的相关文章

[Linux 006]——grep和正则表达式

在使用系统时,我们或多或少的有一些搜索.查找的需求,必须要在文本中搜索某个关键字,或者过滤出文本中某些特定的行.grep 命令就为我们提供了这样一个功能,同时,grep 还可以使用正则表达式进行匹配,这是一个强大的功能,有必要好好掌握. 1.grep 初体验 grep PATTERN [OPTIONS] FILE:在文件中按照模式进行查找.FILE 是我们要查找的目标文件,如果不指定目标文件,grep 将会从标准输入中读取输入的内容,然后进行匹配.为了方便起见,本文的所有演示都在命令行中通过标准

C语言调用正则表达式

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

嵌入式 Linux C语言——C语言基础

嵌入式 Linux C语言--C语言基础 一.数据类型 1.基本数据类型 数据类型是创建变量的模型.变量名是连续存储空间的别名,程序中使用变量命名存储空间,通过变量可以使用存储空间.变量所占的内存大小取决于创建变量的数据类型. 2.有符号和无符号 有符号数中数据类型的最高位用于标识数据的符号,最高位为1表示为负数,最高位为0表示为正数. 计算机中有符号数通常使用补码表示,正数的补码为正数本身,负数的补码为负数的绝对值的各位取反后加1. 计算机中无符号数通常使用原码表示,无符号数默认为正数,没有符

嵌入式linux C++语言(四)——类与对象

嵌入式linux C++语言(四)--类与对象 类的设计和使用如下: #include <iostream>#include <stdlib.h>#include <stdio.h>#include <string.h>using namespace std;class Stack{public:    Stack(int size=1024);    ~Stack();    void init();    bool isEmpty();    bool

嵌入式linux C++语言(七)——继承与派生

嵌入式linux C++语言(七)--继承与派生 一.继承 在C++编程中软件可重用性(software reusability)是通过继承(inheritance)机制来实现的.类的继承,是新的类从已有类那里得到已有的特性.或从已有类产生新类的过程就是类的派生.原有的类称为基类或父类,产生的新类称为派生类或子类. 派生类的声明:class 派生类名:[继承方式] 基类名{派生类成员声明:};    一个派生类可以同时有多个基类,这种情况称为多重继承,派生类只有一个基类,称为单继承. 继承方式规

嵌入式linux C++语言(二)——C++对C语言基础语法的扩展

嵌入式linux C++语言(二)--C++对C语言基础语法的扩展 C++是基于C语言扩展发展而来的面向对象的程序设计语言,本文将主要讨论C++语言基于C语言扩展的方面. 一.类型增强 1.类型检查更严格 在C语言中: const int a = 100; int *p = &a; 在C++语言中: const int a = 100;//必须在定义的时候初始化 const int *p = &a; 在C++语言中不能隐式转换数据类型. error: invalid conversion

嵌入式linux C++语言(一)——C++简介

嵌入式linux C++语言(一)--C++简介 一.C++简介 C语言作是结构化和模块化的语言,适合处理较小规模的程序.对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言并不合适.为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming)思想,支持面向对象的程序设计语言应运而生.Smalltalk 就是当时问世的一种面向对象的语言.在实践工作中,由于C语言的广泛使用,在C语言的基础上根据面向对象的思想发展了C语言,形成了C

嵌入式Linux C语言(三)——指针与函数

嵌入式Linux C语言(三)--指针与函数 指针对函数的功能有巨大的贡献,指针能够将数据传递给函数,并且允许函数对数据进行修改.指针对于函数的作用主要有两方面:将指针传递给函数和声明函数指针. 一.程序的栈和堆 程序的栈和堆是C语言程序运行的运行时元素. 1.程序栈 程序栈是支持函数执行的内存区域,通常和堆共享一块内存区域,通常程序栈占据内存区域的下部,堆用内存区域的上部.程序栈存放栈帧,栈帧存放函数参数和局部变量.调用函数时,函数的栈帧被推倒栈上,栈向上长出一个栈帧,当函数终止时,函数的栈帧

centos下Linux C语言MD5的使用

在Linux C变成中用到MD5加密会使用到openssl库,下面给出的是一个简单的小Demo: #include <stdio.h> #include <openssl/md5.h> #include <string.h> #define MD5_LENGTH 16 #define MAX 40 int main(void) { MD5_CTX ctx; unsigned char data[MAX]; unsigned char md[MD5_LENGTH]; ch