使用POSIX正则库匹配一行中多个结果
正则匹配与正则表达式是什么东西我就不说了,在这里说下POSIX这个c语言正则库在对字符串进行正则匹配时取出多个结果的问题。
首先简单说明下POSIX正则库的几个函数和使用方法
第一个函数:int regcomp(regex_t *preg, const char *regex, int cflags); POSIX C正则库为了提高效率,在将一个字符串与正则表达式进行比较之前,首先要用regcomp()函数对它进行编译,将其转化为regex_t类型。
preg 编译后的regex_t数据
regex 正则表达式
cflags 设置相关标志,包括 REG_EXTENDED、REG_ICASE、REG_NOSUB、REG_NEWLINE
正确编译正则表达式则返回0否则返回一个错误码
第二个函数:int regexec(const regex_t *preg, const char *string, size_t nmatch, regmatch_t pmatch[], int eflags);
该函数的功能就是将编译过后的正则表达式与在进行匹配的字符串进行正则匹配
preg 经regcomp编译后的参数
string 要进行正则匹配的字符串
nmatch pmatch数组的个数
pmatch 该数组中的两个参数rm_so与rm_eo表示匹配之后匹配的字符串在string中的偏移地址的首地址与尾地址
eflags 设置相关标志,包括REG_NOTBOL和REG_NOTEOL
函数返回的结果与regcomp相同
第三个函数:size_t regerror(int errcode, const regex_t *preg, char *errbuf, size_t errbuf_size);
该函数看名字就能猜到,它是通过错误代码返回错误信息的
errcode 是regcomp或者regexec返回的错误码
preg 是经regcomp编译后的数据
errbuf 返回错误信息的缓冲区
errbuf_size 错误信息缓冲区的大小
该函数返回的结果是错误信息的长度
第四个函数:void regfree(regex_t *preg); 很简单,释放内存的
使用这四个函数就可以进行正则匹配了,使用的方法是先使用regcomp对正则表达式进行编译,然后通过regexec进行正则匹配,匹配成功后会在结构体regmatch_t中的两个参数中设置匹配字符串在字符串的起始位置与结束位的偏移量,而传递给regexec中的regmatch_t结构体是一个数组,所以你一定认为标题中获取多个结果的方法就是设置这个数组的大小。起初我也是这么想的,但当我这么做的时候才发现,结果并非这样,一次调用 regexec返回的结果只有一个,没有多个,那么这个结构体数组是怎么回事呢?
man手册中说的是返回的子表达式,这个子表达式是什么?我来举一个例子 假如有这么一个字符串 <title>第一个</title>第二个<title>第三个</title><title>第四个</title> 如何我们想找出这个字符串中界于<title>与</title>之间中的字符串也就是字符串”第一个、第三个、第四个”,那么我们可能会通过这个正则表达式来获取想要的字符串 <title>.[^>]* ,但这样获取的字符串中保有所以为了只取<title>与</title>中间的字符串我们可以使用子表达式来完成,那么这样这个正则表达式就是<title>\(.[^>]*\)
这样我们就可以通过pmatch[1]来定位取得的子表达式中相对于字符串的偏移,所以显然,pmatch[2]表示的是第二个子表达式,pmatch[3]表示的是第三个子表达式,而pmatch[0]则是整个正则表达式匹配的结果,于是想要在字符串中匹配出所有的<title>与</title>中的结果时就需要多次调用regexec函数。
既然已经知道了regmatch_t这个结构体数组并不像我们想象中的那样可以返回匹配的多个结果,那么如何在一个字符串中进行多次的 regexec调用进行匹配呢?这其实很简单,如何我们需要匹配的字符串是多行的,那么可以按着每一行调用一次regexec进行匹配,但你也看到了,就像上面的例子,你会很有可能在一行中匹配出多个结果,所以我们需要另一种方法,其实也很简单,我们对字符串的指针进行位移形成一个”新的字符串”就可以了。
因为regexec匹配后在pmatch[0]中很返回此次匹配字符串的未位偏移量,所以我们只需要将需要进行匹配的字符串的首地址移动到该处并再次进行下一次regexec的调用匹配就可以了,并如此循环,只到全部匹配完成。以上面的例子来说
<title>第一个</title>第二个</title><title>第三个</title><title>第四个</title>
第一次匹配时匹配到的结果是<title>第一个,此时pmatch[0].rm_eo中的数字为 strlen(“<title>第一个”),所以我们将这个字符串的首地址进行移动,将首地址移动到<title>第一个后面,这时”新的字符串”就是
</title>第二个</title><title>第三个</title><title>第四个</title>
如此类推就可以匹配到所有的结果。
最后以一个匹配百度贴吧的发帖时间与帖子标题为例给出一个c语言源代码
- #include <curl/curl.h>
- #include <stdlib.h>
- #include <sys/types.h>
- #include <string.h>
- #include <regex.h>
- #include <iconv.h>
- #include <errno.h>
- typedef struct
- {
- int len; //当前字符串data的长度
- char *data; //返回的字符串
- }DATA; //保存libcurl回调函数中返回的数据
- typedef struct node
- {
- char *url;
- char *topic;
- struct node *next;
- }LIST; //一个链表结构,用来缓存第一次扫描贴吧时帖子的地址与标题
- size_t get_data(char *ptr,size_t size,size_t nmemb,DATA *data) //libcurl返回数据的回调函数,使用动态字符串的方式保存返回的数据
- {
- if(data->len)
- {
- char *temp;
- temp=malloc(data->len+1);
- snprintf(temp,data->;len+1,"%s",data->data);
- free(data->data);
- data->data=malloc(data->len+nmemb+1);
- snprintf(data->data,data->len+nmemb+1,"%s%s",temp,ptr);
- free(temp);
- }
- else
- {
- data->data=malloc(nmemb+1);
- snprintf(data->data,nmemb+1,"%s",ptr);
- }
- data->len+=nmemb;
- return nmemb;
- }
- LIST *list_init(void) //链表的初始化
- {
- LIST *list;
- list=malloc(sizeof(LIST));
- list->url=NULL;
- list->topic=NULL;
- list->next=NULL;
- return list;
- }
- void list_add(LIST *list,char *url,char *topic) //将链表中添加数据
- {
- LIST *temp;
- while(list->next)
- list=list->next;
- temp=malloc(sizeof(LIST));
- temp->url=malloc(strlen(url)+1);
- snprintf(temp->url,strlen(url)+1,"%s",url);
- temp->topic=malloc(strlen(topic)+1);
- snprintf(temp->topic,strlen(topic)+1,"%s",topic);
- temp->next=NULL;
- list->next=temp;
- }
- void list_destroy(LIST *list) //销毁链表,释放内存
- {
- LIST *temp;
- while(list->next)
- {
- list=list->next;
- temp=list;
- free(list->url);
- free(list->topic);
- free(temp);
- }
- }
- int gbk_to_utf8(char *in,char *out,size_t out_bytes) //由于百度贴吧使用的是GBK编码,而我本机是使用UTF-8编码的,所以需要进行编码转换
- {
- iconv_t cd;
- size_t in_bytes=strlen(in);
- if((cd=iconv_open("UTF-8//","GBK//IGNORE")) == (iconv_t)-1)
- return -1;
- if(iconv(cd,&in,&in_bytes,&out,&out_bytes) == -1)
- return -1;
- iconv_close(cd);
- return 0;
- }
- LIST *topic_list(char *str) //第一次扫描百度贴吧,获取url与标题并缓存在链表中
- {
- LIST *list=NULL;
- regex_t reg;
- regmatch_t pmatch[3]; //两个子表达式
- char topic[1024]={0};
- char url[128]={0};
- char temp[1024]={0};
- list=list_init();
- if(regcomp(®,"href=\"\\(/p/[0-9]\\{6,10\\}\\)\" title=\"\\(.[^\"]*\\)",0) != 0) //对贴吧中含有帖子与帖子链接的字符进行匹配的正则表达式,并使用子表达式匹配出链接与标题
- {
- perror("regcomp"); //这里只是简单地debug,正确的提取错误的方法是使用regerror
- regfree(®);
- return NULL;
- }
- while(regexec(®,str,3,pmatch,0) == 0) //循环匹配
- {
- bzero(url,sizeof(url));
- snprintf(url,pmatch[1].rm_eo-pmatch[1].rm_so+23,
- "http://tieba.baidu.com%s",str+pmatch[1].rm_so); //取出第一个子表达式即帖子链接地址
- bzero(temp,sizeof(temp));
- snprintf(temp,pmatch[2].rm_eo-pmatch[2].rm_so+1,
- "%s",str+pmatch[2].rm_so); //取出第二个子表达式结果即帖子的标题
- bzero(topic,sizeof(topic));
- gbk_to_utf8(temp,topic,sizeof(topic)-1); //进行编码转换
- list_add(list,url,topic); //将结果缓存到链表中
- str+=pmatch[0].rm_eo; //移动字符串首地址到前面匹配的结果的未尾
- }
- regfree(®);
- return list;
- }
- void list_print(LIST *list) //打印链表中的内容
- {
- while(list->next)
- {
- list=list->next;
- printf("%s %s\n",list->url,list->topic);
- }
- }
- void print_topic_and_post_time(CURL *curl,LIST *list,char *type) //打印标题与发帖时间的函数
- {
- regex_t reg;
- regmatch_t pmatch[1];
- char temp[20];
- DATA data;
- LIST *head;
- if(strcmp(type,"1") == 0)
- head=list_init();
- curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data);
- if(regcomp(®,"[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}",0) != 0) //匹配出发帖时间的正则表达式
- return;
- while(list->next) //从缓存链表中取出链接进行访问并匹配出发帖时间
- {
- list=list->next;
- curl_easy_setopt(curl,CURLOPT_URL,list->url);
- data.data=NULL;
- data.len=0;
- curl_easy_perform(curl);
- if(data.len)
- {
- bzero(temp,sizeof(temp));
- if(regexec(®,data.data,1,pmatch,0) != 0)
- snprintf(temp,sizeof(temp),"未知发表时间");
- else
- snprintf(temp,pmatch[0].rm_eo-pmatch[0].rm_so+1,"%s",data.data+pmatch[0].rm_so); //取出发匹配结果
- if(strcmp(type,"1") == 0) // 由于获取帖子标题和发帖时间时链接不同,所以要分两步进行访问,第一步访问缓存地址与标题,第二步访问缓存中缓存的链接,所以在时间上可能会很长,百度贴吧一页的帖子数量在50帖,这里采用两种方法进行输出,一、访问所有帖子链接并缓存到链表中然后一次性打印,二、每访问一个帖子链接便打印一次,显然由于数量过多,前种方法在等待屏幕的输出方面需要一定的时间
- list_add(head,temp,list->topic);
- else
- printf("%s %s\n",temp,list->topic);
- free(data.data);
- }
- }
- regfree(®);
- if(strcmp(type,"1") == 0)
- {
- list_print(head);
- list_destroy(head);
- }
- }
- int main(int argc,char **argv)
- {
- CURL *curl;
- CURLcode code;
- char tieba[128]={0};
- DATA data;
- LIST *list;
- data.len=0;
- data.data=NULL;
- if(argc < 3)
- {
- printf("tieba [1|an other]\n");
- return -1;
- }
- snprintf(tieba,sizeof(tieba),"http://tieba.baidu.com/f?kw=%s",argv[1]); //贴吧链接
- curl=curl_easy_init();
- curl_easy_setopt(curl,CURLOPT_URL,tieba);
- curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,get_data); //设置回调函数
- curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data); //设置回调函数的参数
- code=curl_easy_perform(curl);
- if(code != 0)
- return -1;
- if(data.len)
- {
- list=topic_list(data.data); //缓存帖子标题与地址
- free(data.data);
- if(list)
- {
- print_topic_and_post_time(curl,list,argv[2]); //打印帖子标题与发帖时间
- list_destroy(list);
- }
- }
- curl_easy_cleanup(curl);
- return 0;
- }
http://blog.163.com/lixiangqiu_9202/blog/static/53575037201412311211291/
使用POSIX正则库匹配一行中多个结果的更多相关文章
- posix 正则库程序
使用的是posix 正则库,参考: http://see.xidian.edu.cn/cpp/html/1428.html 执行匹配的时: gcc myreg.c ip.pat 内容: ip.*[0- ...
- [02]APUE:POSIX 正则库(#include <regex.h>)
正则匹配流程: 声明一个 regex_t 类型的变量(结构体) regcomp 函数会将“正则匹配条件”写入此结构体,并编译成特定的二进制格式(加快匹配速度) 声明一个 regmatch_t 类型的变 ...
- 匹配 $gdinfo 中的数字
<?php /* //下载 $filename = 'av.zip'; // 设置类型:可以省略 // header("content-type:image/jpeg"); ...
- 【归纳】正则表达式及Python中的正则库
正则表达式 正则表达式30分钟入门教程 runoob正则式教程 正则表达式练习题集(附答案) 元字符\b代表单词的分界处,在英文中指空格,标点符号或换行 例子:\bhi\b可以用来匹配hi这个单词,且 ...
- 正则双重过滤 /// splitKey1 第一个正则式匹配 /// splitKey2 匹配结果中再次匹配进行替
/// <summary> /// 正则双重过滤 /// splitKey1 第一个正则式匹配 /// splitKey2 匹配结果中再次匹配进行替换 /// </summary&g ...
- PHP用正则匹配字符串中的特殊字符防SQL注入
本文出至:新太潮流网络博客 /** * [用正则匹配字符串中的特殊字符] * @E-mial wuliqiang_aa@163.com * @TIME 2017-04-07 * @WEB http:/ ...
- mysql中的正则操作 匹配手机号,匹配中文,替换
mysql中的正则操作 匹配手机号,匹配中文,替换 正则匹配hy_user表内tel字段的电话号码: SELECT * FROM hy_user WHERE tel REGEXP "[1][ ...
- ASP.NET_正则表达式_匹配HTML中的一行或多行
一.匹配数字串/flash/([0-9]+).htm 二.匹配不含双引号的字符串<p class=\"w490\">([^\"]+)</p> 三. ...
- C正则库做DNS域名验证时的性能对比
C正则库做DNS域名验证时的性能对比 本文对C的正则库regex和pcre在做域名验证的场景下做评测. 验证DNS域名的正则表达式为: "^[0-9a-zA-Z_-]+(\\.[0-9a ...
随机推荐
- TortoiseGit 连接每次都要输入用户名和密码
当你配置好git后,在C:\Documents and Settings\Administrator\ 或者 C:\Users\Administrator 目录下有一个 .gitconfig 的文件 ...
- 【Java】一个小程序,计算它包含的代码所需的耗时
写一个小程序,用来计算它包含的代码所需的耗时.虽然简单,测试代码是否耗时还是有点用的,不用重新写嘛~ import java.util.Date; import java.util.concurren ...
- 解决一道leetcode算法题的曲折过程及引发的思考
写在前面 本题实际解题过程是 从 40秒 --> 24秒 -->1.5秒 --> 715ms --> 320ms --> 48ms --> 36ms --> ...
- GPIO相关知识
参考资料: 1. 维基百科GPIO 2. GPIO博客资料(一) 3. MMIO和PMIO 知识点: ● GPIO是General-purpose input/output的缩写,是一个在集成电路上的 ...
- Nginx模块之————RTMP模块在Ubuntu 14.04年的设置与搭建
Nginx的设置,RTMP在Ubuntu 14.04 https://www.vultr.com/docs/setup-nginx-rtmp-on-ubuntu-14-04
- hdu3652 B-number
链接 题意求能够整除和包含13的数字. 这个比较简单,保留余数及1,然后标记前面是否出现过13就行. #include <iostream> #include<cstdio> ...
- Linux shell中运行命令后加上字符“&”的作用
上午登录服务器编译运行服务端程序的时候,学到了在命令后加上字符“&”后,退出shell,运行的命令可以继续运行.不解原因,并到网上搜索了以下,明白了点! 以下是搜索到的片段: & 放在 ...
- jquery validate 指定错误内容的位置
一.默认的提示 messages: { required: "This field is required.", remote: "Please fix this fie ...
- 利用react来制作评论框
学习地址:https://my.oschina.net/leogao0816/blog/379488
- 【转】PDF电子书分享
http://www.voidcn.com/blog/u013830841/article/p-4343018.html http://www.voidcn.com/blog/u013830841/a ...