Regex in C
正则表达式在处理文本方面十分强大,几乎稍微复杂一些的文本操作都会用到,而在python和java中使用regex都十分方便,其实Linux中的C也提供也标准库,使用也不会很复杂。遗憾的是功能较java和python的regex库要少,缺乏替换等功能,需要使用者自己去实现。
在Linux下输入man regcomp
就可以查看所有与regex相关的函数的说明了,以下是一些关键信息的摘要,如果使用中遇到问题请自行查阅manual。
编译模式
1
| int regcomp(regex_t *preg, const char *regex, int cflags);
|
regcomp用来编译文本regex生成匹配规则,执行结果保存在regex_t
结构的preg
参数中,执行成功时返回0。
由于使用java,python较多,下意识地将C函数库与java、python类库比较,C语言中常常使用结构体来实现OOP语言中与对象类似的功能。但是C语言函数调用使用值传递,且结构体与数组的定义形式与普通变量相同,数据都保存在栈中,而不是堆中(java所有的对象和数组都是new
操作建立的,保存于堆中),所以常常看到将指针作为参数传入函数,但实际担当的是传递结果的角色。
执行匹配
1 2
| int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
|
regexec
将匹配规则与目标文本进行匹配,并将匹配结果保存在regmatch_t
的结构数组pmatch[]
中。
匹配结果
正regmatch_t
的结构如下:
1 2 3 4
| typedef struct { regoff_t rm_so; regoff_t rm_eo; } regmatch_t;
|
rm_so
,rm_eo
分别记录了匹配成功字段的起始(第一个匹配字符)和结束点(结尾的下一个字符)的偏移量。
正模式中如果有分组存在,那么将以类似$&,$1,$2…的顺序将结果存储到regmatch_t
数组中。
错误信息收集
当compile或execute出错时,可以使用以下函数获取错误信息。
1 2
| size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
|
正参数中需要提供错误码和一个字符数组缓存,返回错误信息的长度。
正操作完成后,记得匹配模式的内存空间。
释放空间
1
| void regfree(regex_t *preg);
|
例程
下面用一个完整的例子进行学习和总结。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| #include <stdio.h> #include <stdlib.h> #include <regex.h> #include <sys/types.h> #include <string.h> #define MAX_ERROR_MSG 0x1000 int main() { regex_t *r = malloc(sizeof(regex_t)); //分配编译后模式的存储空间 //regex_t *r; //使用这句代替上句是不行的,因为指针未初始化 char *text = "room 10, dorm 18."; //目标文本 char *regtxt = "[a-z]+ ([0-9]+)"; //模式 int status = regcomp(r, regtxt, REG_EXTENDED | REG_NEWLINE); //编译模式 if (status) { //处理可能的错误 char error_message[MAX_ERROR_MSG]; regerror(status, r, error_message, MAX_ERROR_MSG); printf("Regex error compiling '%s': %s\n", text, error_message); return 1; } size_t nmatch = 2; //保存结果,每次匹配有两组$&,$1结果,分别是整体与子匹配 regmatch_t m[nmatch]; char *p = text; while (1) { //连续匹配直到行尾 status = regexec(r, p, nmatch, m, 0); //匹配操作 if (status) { //判断结束或错误 char error_message[MAX_ERROR_MSG]; regerror(status, r, error_message, MAX_ERROR_MSG); printf("Regex stop executing '%s': %s\n", text, error_message); return 1; } int i; for (i = 0; i < nmatch; i++) { //打印结果,注意regmatch_t中保存的偏移信息 printf("%.*s, so = %d, eo = %d\n", m[i].rm_eo - m[i].rm_so, p + m[i].rm_so, m[i].rm_so, m[i].rm_eo); } p += m[0].rm_eo; } regfree(r); //释放空间 return 0; }
|
总结
总的来看C中的regex库使用流程与其它高级语言差别不大,但是结构体和指针的使用使得操作要麻烦一些。最后regex标准库提供的函数只有compile, execute, free再加上error四个操作,类似循环匹配、字符替代的功能,就需要自己在此基础上实现了。