正则匹配与正则表达式是什么东西我就不说了,在这里说下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 <stdio.h>
  1. #include <curl/curl.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <string.h>
  5. #include <regex.h>
  6. #include <iconv.h>
  7. #include <errno.h>
  8.  
  9. typedef struct
  10. {
  11.  int len; //当前字符串data的长度
  12.  char *data; //返回的字符串
  13. }DATA; //保存libcurl回调函数中返回的数据
  14.  
  15. typedef struct node
  16. {
  17.  char *url;
  18.  char *topic;
  19.  
  20.  struct node *next;
  21. }LIST; //一个链表结构,用来缓存第一次扫描贴吧时帖子的地址与标题
  22.  
  23. size_t get_data(char *ptr,size_t size,size_t nmemb,DATA *data) //libcurl返回数据的回调函数,使用动态字符串的方式保存返回的数据
  24. {
  25.  if(data->len)
  26.  {
  27.   char *temp;
  28.  
  29.   temp=malloc(data->len+1);
  30.   snprintf(temp,data->;len+1,"%s",data->data);
  31.   free(data->data);
  32.  
  33.   data->data=malloc(data->len+nmemb+1);
  34.   snprintf(data-&gt;data,data->len+nmemb+1,"%s%s",temp,ptr);
  35.   free(temp);
  36.  }
  37.  else
  38.  {
  39.   data->data=malloc(nmemb+1);
  40.   snprintf(data->data,nmemb+1,"%s",ptr);
  41.  }
  42.  
  43.  data->len+=nmemb;
  44.  return nmemb;
  45. }
  46.  
  47. LIST *list_init(void) //链表的初始化
  48. {
  49.  LIST *list;
  50.  
  51.  list=malloc(sizeof(LIST));
  52.  list->url=NULL;
  53.  list->topic=NULL;
  54.  list->next=NULL;
  55.  
  56.  return list;
  57. }
  58.  
  59. void list_add(LIST *list,char *url,char *topic) //将链表中添加数据
  60. {
  61.  LIST *temp;
  62.  
  63.  while(list->next)
  64.   list=list->next;
  65.  
  66.  temp=malloc(sizeof(LIST));
  67.  temp->url=malloc(strlen(url)+1);
  68.  snprintf(temp->url,strlen(url)+1,"%s",url);
  69.  temp->topic=malloc(strlen(topic)+1);
  70.  snprintf(temp->topic,strlen(topic)+1,"%s",topic);
  71.  
  72.  temp->next=NULL;
  73.  list->next=temp;
  74. }
  75.  
  76. void list_destroy(LIST *list) //销毁链表,释放内存
  77. {
  78.  LIST *temp;
  79.  
  80.  while(list->next)
  81.  {
  82.   list=list->next;
  83.  
  84.   temp=list;
  85.   free(list->url);
  86.   free(list->topic);
  87.   free(temp);
  88.  }
  89. }
  90.  
  91. int gbk_to_utf8(char *in,char *out,size_t out_bytes) //由于百度贴吧使用的是GBK编码,而我本机是使用UTF-8编码的,所以需要进行编码转换
  92. {
  93.  iconv_t cd;
  94.  size_t in_bytes=strlen(in);
  95.  
  96.  if((cd=iconv_open("UTF-8//","GBK//IGNORE")) == (iconv_t)-1)
  97.   return -1;
  98.  if(iconv(cd,&in,&in_bytes,&out,&out_bytes) == -1)
  99.   return -1;
  100.  
  101.  iconv_close(cd);
  102.  
  103.  return 0;
  104. }
  105.  
  106. LIST *topic_list(char *str) //第一次扫描百度贴吧,获取url与标题并缓存在链表中
  107. {
  108.  LIST *list=NULL;
  109.  regex_t reg;
  110.  regmatch_t pmatch[3]; //两个子表达式
  111.  char topic[1024]={0};
  112.  char url[128]={0};
  113.  char temp[1024]={0};
  114.  
  115.  list=list_init();
  116.  
  117.  if(regcomp(&reg,"href=\"\\(/p/[0-9]\\{6,10\\}\\)\" title=\"\\(.[^\"]*\\)",0) != 0) //对贴吧中含有帖子与帖子链接的字符进行匹配的正则表达式,并使用子表达式匹配出链接与标题
  118.  {
  119.   perror("regcomp"); //这里只是简单地debug,正确的提取错误的方法是使用regerror
  120.   regfree(&reg);
  121.   return NULL;
  122.  }
  123.  while(regexec(&reg,str,3,pmatch,0) == 0) //循环匹配
  124.  {
  125.   bzero(url,sizeof(url));
  126.   snprintf(url,pmatch[1].rm_eo-pmatch[1].rm_so+23,
  127.     "http://tieba.baidu.com%s",str+pmatch[1].rm_so); //取出第一个子表达式即帖子链接地址
  128.   bzero(temp,sizeof(temp));
  129.   snprintf(temp,pmatch[2].rm_eo-pmatch[2].rm_so+1,
  130.     "%s",str+pmatch[2].rm_so); //取出第二个子表达式结果即帖子的标题
  131.  
  132.   bzero(topic,sizeof(topic));
  133.   gbk_to_utf8(temp,topic,sizeof(topic)-1); //进行编码转换
  134.   list_add(list,url,topic); //将结果缓存到链表中
  135.  
  136.   str+=pmatch[0].rm_eo; //移动字符串首地址到前面匹配的结果的未尾
  137.  }
  138.  regfree(&reg);
  139.  
  140.  return list;
  141. }
  142.  
  143. void list_print(LIST *list) //打印链表中的内容
  144. {
  145.  while(list->next)
  146.  {
  147.   list=list->next;
  148.  
  149.   printf("%s %s\n",list->url,list->topic);
  150.  }
  151. }
  152.  
  153. void print_topic_and_post_time(CURL *curl,LIST *list,char *type) //打印标题与发帖时间的函数
  154. {
  155.  regex_t reg;
  156.  regmatch_t pmatch[1];
  157.  char temp[20];
  158.  DATA data;
  159.  LIST *head;
  160.  
  161.  if(strcmp(type,"1") == 0)
  162.   head=list_init();
  163.  curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data);
  164.  if(regcomp(&reg,"[0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\} [0-9]\\{2\\}:[0-9]\\{2\\}",0) != 0) //匹配出发帖时间的正则表达式
  165.   return;
  166.  
  167.  while(list->next) //从缓存链表中取出链接进行访问并匹配出发帖时间
  168.  {
  169.   list=list->next;
  170.  
  171.   curl_easy_setopt(curl,CURLOPT_URL,list->url);
  172.   data.data=NULL;
  173.   data.len=0;
  174.   curl_easy_perform(curl);
  175.  
  176.   if(data.len)
  177.   {
  178.    bzero(temp,sizeof(temp));
  179.  
  180.    if(regexec(&reg,data.data,1,pmatch,0) != 0)
  181.     snprintf(temp,sizeof(temp),"未知发表时间");
  182.    else
  183.     snprintf(temp,pmatch[0].rm_eo-pmatch[0].rm_so+1,"%s",data.data+pmatch[0].rm_so); //取出发匹配结果
  184.  
  185.    if(strcmp(type,"1") == 0) // 由于获取帖子标题和发帖时间时链接不同,所以要分两步进行访问,第一步访问缓存地址与标题,第二步访问缓存中缓存的链接,所以在时间上可能会很长,百度贴吧一页的帖子数量在50帖,这里采用两种方法进行输出,一、访问所有帖子链接并缓存到链表中然后一次性打印,二、每访问一个帖子链接便打印一次,显然由于数量过多,前种方法在等待屏幕的输出方面需要一定的时间
  186.     list_add(head,temp,list->topic);
  187.    else
  188.     printf("%s %s\n",temp,list->topic);
  189.  
  190.    free(data.data);
  191.   }
  192.  }
  193.  regfree(&reg);
  194.  
  195.  if(strcmp(type,"1") == 0)
  196.  {
  197.   list_print(head);
  198.   list_destroy(head);
  199.  }
  200. }
  201.  
  202. int main(int argc,char **argv)
  203. {
  204.  CURL *curl;
  205.  CURLcode code;
  206.  char tieba[128]={0};
  207.  DATA data;
  208.  LIST *list;
  209.  
  210.  data.len=0;
  211.  data.data=NULL;
  212.  if(argc < 3)
  213.  {
  214.   printf("tieba  [1|an other]\n");
  215.   return -1;
  216.  }
  217.  snprintf(tieba,sizeof(tieba),"http://tieba.baidu.com/f?kw=%s",argv[1]); //贴吧链接
  218.  curl=curl_easy_init();
  219.  
  220.  curl_easy_setopt(curl,CURLOPT_URL,tieba);
  221.  curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,get_data); //设置回调函数
  222.  curl_easy_setopt(curl,CURLOPT_WRITEDATA,&data); //设置回调函数的参数
  223.  code=curl_easy_perform(curl);
  224.  
  225.  if(code != 0)
  226.   return -1;
  227.  if(data.len)
  228.  {
  229.   list=topic_list(data.data); //缓存帖子标题与地址
  230.   free(data.data);
  231.   if(list)
  232.   {
  233.    print_topic_and_post_time(curl,list,argv[2]); //打印帖子标题与发帖时间
  234.    list_destroy(list);
  235.   }
  236.  }
  237.  
  238.  curl_easy_cleanup(curl);
  239.  return 0;
  240. }

http://blog.163.com/lixiangqiu_9202/blog/static/53575037201412311211291/

使用POSIX正则库匹配一行中多个结果的更多相关文章

  1. posix 正则库程序

    使用的是posix 正则库,参考: http://see.xidian.edu.cn/cpp/html/1428.html 执行匹配的时: gcc myreg.c ip.pat 内容: ip.*[0- ...

  2. [02]APUE:POSIX 正则库(#include <regex.h>)

    正则匹配流程: 声明一个 regex_t 类型的变量(结构体) regcomp 函数会将“正则匹配条件”写入此结构体,并编译成特定的二进制格式(加快匹配速度) 声明一个 regmatch_t 类型的变 ...

  3. 匹配 $gdinfo 中的数字

    <?php /* //下载 $filename = 'av.zip'; // 设置类型:可以省略 // header("content-type:image/jpeg"); ...

  4. 【归纳】正则表达式及Python中的正则库

    正则表达式 正则表达式30分钟入门教程 runoob正则式教程 正则表达式练习题集(附答案) 元字符\b代表单词的分界处,在英文中指空格,标点符号或换行 例子:\bhi\b可以用来匹配hi这个单词,且 ...

  5. 正则双重过滤 /// splitKey1 第一个正则式匹配 /// splitKey2 匹配结果中再次匹配进行替

    /// <summary> /// 正则双重过滤 /// splitKey1 第一个正则式匹配 /// splitKey2 匹配结果中再次匹配进行替换 /// </summary&g ...

  6. PHP用正则匹配字符串中的特殊字符防SQL注入

    本文出至:新太潮流网络博客 /** * [用正则匹配字符串中的特殊字符] * @E-mial wuliqiang_aa@163.com * @TIME 2017-04-07 * @WEB http:/ ...

  7. mysql中的正则操作 匹配手机号,匹配中文,替换

    mysql中的正则操作 匹配手机号,匹配中文,替换 正则匹配hy_user表内tel字段的电话号码: SELECT * FROM hy_user WHERE tel REGEXP "[1][ ...

  8. ASP.NET_正则表达式_匹配HTML中的一行或多行

    一.匹配数字串/flash/([0-9]+).htm 二.匹配不含双引号的字符串<p class=\"w490\">([^\"]+)</p> 三. ...

  9. C正则库做DNS域名验证时的性能对比

    C正则库做DNS域名验证时的性能对比   本文对C的正则库regex和pcre在做域名验证的场景下做评测. 验证DNS域名的正则表达式为: "^[0-9a-zA-Z_-]+(\\.[0-9a ...

随机推荐

  1. IntelliJ IDEA使用(1)——IDEA配置JDK

    提前安装jdk,配置环境变量 一.配置jdk 1.依次点开File -->Project Structure,点击左侧标签页,点击SDKs 2.点击+号,选SDK 3.在弹出框选择jdk路径(我 ...

  2. ie7 父元素宽度自适应且为浮动的话 子元素的宽度将不能按比例设置问题

    好久没切图,昨天遇到个浏览器兼容的老问题,在ie7下,父元素设置浮动后,其宽度是自适应的,子元素的宽度若没有确定则将显示最小宽度,即文本所占的宽度. 正常其他浏览器显示如下: ie7中显示效果如下: ...

  3. U9单据UI开发--单据类型UI开发

    1.在解决方案下新建UI界面项目,命名以UI作为后缀 2.先删除系统默认新建的UI界面数据模型,并新建界面数据 3.新建单据类型UIModel(界面数据),以model作为界面数据后缀名 4.修改单据 ...

  4. 单据类型BE构建

    开发流程:BE--UI--发布在开发时有必要对元数据.代理调用.弱类型.强类型有所了解基本的单据开发包括单据类型BE.单据状态BE.单据BE,下面分别罗列出各单据的BE的设计开发: 本章主要讲解单据类 ...

  5. Linux 下 git连接github的使用

    1.安装git sudo apt-get install git 2.创建github帐号 3.Linux创建SSH密钥: ssh-keygen  //一直默认 4.将公钥加入到Github账户信息A ...

  6. HR函数学习02——分配组织单位

    REPORT ZLYHR01. "创建组织单元 成功 DATA:LS_OBJ TYPE OBJEC, LV_STU TYPE GDSTR-SVECT, LV_TIT TYPE CHAR20, ...

  7. MySQL数据库管理用户权限

    http://blog.itpub.net/7607759/viewspace-675079/ 2.2 授予权限 前面提到了grant命令,grant的语法看起来可是相当复杂的呐: GRANT pri ...

  8. Windows“神器”收集贴

    本文本来是刚开始发现autohotkey时比较兴奋,收集了几个autohotkey的介绍页面.最近又发现了win下多桌面的神器virtuawin,心想干脆在把本帖改成专门收集win下神器的帖子吧.如果 ...

  9. iOS开发网络篇—使用ASI框架进行文件下载

    iOS开发网络篇—使用ASI框架进行文件下载 说明:本文介绍iOS网络编程中经常用到的框架ASI,如何使用该框架进行文件的下载. 一.简单介绍 代码示例: #import "YYViewCo ...

  10. Linux准确获取IP

    有时搞一些跨网段的工程和应用,需要尽量准确的知道电信.网通.铁通等电信运营商的IP地址段分配情况,可网上的资料不但很少,而且经常都是N个月前的过期资料…… APNIC是管理亚太地区IP地址分配的机构, ...