正则匹配与正则表达式是什么东西我就不说了,在这里说下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. Cheatsheet: 2015 05.01 ~ 05.31

    .NET .NET on Mac for the OS X n00b without Mono via Visual Studio Code Microsoft frameworks deprecat ...

  2. Displaying Window In Center In Oracle Forms 6i

    Center window automatically  in Oracle Forms 6i, use the following procedure by passing window name ...

  3. (二)Kafka动态增加Topic的副本(Replication)

    (二)Kafka动态增加Topic的副本(Replication) 1. 查看topic的原来的副本分布 [hadoop@sdf-nimbus-perf ~]$ le-kafka-topics.sh ...

  4. 《K&R》里贯穿全书的代码

    个人阅读<K&R>的感觉就是:前后内容联系特别紧密,前面的代码没有理解好到了后面就看不下去. 1.getline(char s[], int lim) 调用结果:往参数数组中读入字 ...

  5. 解决 Cannot find OpenSSL's <evp.h>

    yum install openssl openssl-devel ln -s /usr/lib64/libssl.so /usr/lib/

  6. 转:C++的重载(overload)与重写(override)

    C++ override overload 的区别  override是指在不同作用域中,多个函数原型完全一样,而实现却不同的函数.在C++中,经常发生在类的继承中.当基类中的某个方法是virtual ...

  7. 【转】selenium之 定位以及切换frame

    转载自:http://www.voidcn.com/blog/huilan_same/article/p-6155896.html 很多人在用selenium定位页面元素的时候会遇到定位不到的问题,明 ...

  8. zoj 2833 friendship

    zoj 2833这次真的很顺利了..居然是因为数组的大小没有符合要求,瞎折腾了很久..没有注意到要求范围,真是该死! 想法很简单,就是定义一个父结点数组,下标 i 表示这个元素,初始化为 -1表示 这 ...

  9. CSS布局:两列,左边宽度自适应,右边宽度固定200px

    <div id="box1"> <div id="left1"></div> <div id="right1 ...

  10. F2工作流引擎之 工作流运转模型(三)

    1流程单起点单终止模型 单起点:一个流程定义必须有且唯一起点 单结束点:一个流程定义必须有且唯一结束点. 约定:提单与结束是每个流程必须有的活动,且唯一只有一个提单和结束. 2串行模型 描述:串行(S ...