鱼C论坛

 找回密码
 立即注册
查看: 1918|回复: 1

[技术交流] 小说追更器——regex库函数的练习

[复制链接]
发表于 2019-11-17 17:20:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
本帖最后由 cnjn 于 2019-11-17 17:31 编辑

写这个软件就是为了练习C语言下的正则表达式处理
主要解决了获取子匹配文本的获取,倒序正则匹配文本,以及。。。该死的内泄漏
泄漏直接导致了我竟然无法使用fputs(),当我用gdb单步调试是fputs()崩溃的时候差点没把眼珠子瞪出来。。。它是库函数呐。。。它崩了我是没点办法诶后面才发现是自己写的代码太菜了。。。

内存泄漏是因为涉及到中文字符串的处理,strlen()求出来的长度似乎有点问题,,,所以,我用了一个非常笨的法子,就是一律malloc(1000000)。。。



下面贴出代码:
  1. #include<stdio.h>
  2. #include<stdlib.h>
  3. #include<regex.h>
  4. #include<string.h>
  5. #include"strrpc.h"
  6. #define MAX 1000000


  7. /*

  8. 程序的思路:
  9.     main函数调用wget保存章节目录的网页源码到tmp;
  10.     main()调用compile()匹配出章节名及其url
  11.     打印出章节名,分为最新章节及最近章节
  12.     调用read_novel(),其中ch为chapters数组中的下标序号
  13.     read_novel()调用wget获取章节正文的网页源码保存到tmp2
  14.     read_novel()调用get_novel()提取出tmp2的内容并保存到tmp3
  15.     read_novel()调用cat显示出处理好的章节
  16.     read_novel()开始调用自身,也就是开始递归,以此实现上一章和下一章的切换,嗯。。。其实仔细观察就会发现这个函数是有递无归的(手动捂脸),因为我会强行直接退出软件


  17.     最最重要的是:我没有释放内存!我没有释放内存!我没有释放内存!  重要的事儿说三遍
  18.     大家千万不要学我,就是因此才会无法发现内存泄露,我被这个问题卡了5天呐!以后一定不敢只申请不释放了

  19. */


  20. struct Chapters{
  21.     char name[100];
  22.     char url[100];
  23. } chapters[7];//用来存储章节名及其url

  24. struct Compile{
  25.         char *body;
  26.         size_t size;
  27.         int child_num;
  28.         char **child;//嗯。。。这里应该是substring吧,就是子匹配文本
  29.         regoff_t rm_eo;
  30. };//正则匹配的结果;body是匹配到的文本;child是子匹配文本数组,size是body+child的大小,包括所有的child。。。;rm_eo是匹配文本的结束下标,可用于多次匹配

  31. char *inverse(char *str){
  32.         char *res=malloc(strlen(str)+1);
  33.         int len=strlen("啊"),i=0,j=strlen(str)-1;
  34.         while(str[i]!='\0'){
  35.                 if(str[i]>0x7F){
  36.                         strncpy(&res[j-len+1],&str[i],len);
  37.                         i+=len;
  38.                         j-=len;
  39.        
  40.                 }else{
  41.                         strncpy(&res[j],&str[i],1);
  42.                         i++;
  43.                         j--;
  44.                 }
  45.         }
  46.         res[strlen(str)]='\0';
  47.         return res;
  48. }//我自己写的一个字符串颠倒函数,因为网上分享的太不友好了。。。可处理中英文混合字符串,用于倒序正则匹配

  49. struct Compile compile(int child_num,char *pattern,char *content){
  50.     //child_num代表子匹配文本的数量,pattern代表正则表达式,content表示被匹配文本
  51.     regex_t preg;
  52.     regcomp(&preg,pattern,REG_EXTENDED|REG_NEWLINE);
  53.     regmatch_t matched[child_num+1];
  54.     int vv=regexec(&preg,content,child_num+1,matched,REG_NOTBOL);
  55.     if(vv!=0){
  56.             char tmp[500];
  57.         regerror(vv,&preg,tmp,500);
  58.         puts(tmp);
  59.         exit(-1);
  60.     }
  61.     struct Compile res;
  62.     res.body=malloc(MAX);
  63.     res.child=(char **)calloc(1,sizeof(char *));//这里child只分配了内存放指针,没有为具体的子匹配文本分配内存,因为body和child其实是共用一段内存的,中间用'\0'分开罢了
  64.     res.size=0;
  65.     res.child_num=0;
  66.     char *pos=res.body;
  67.     for (int i=0;i<=child_num;i++){
  68.             int len=matched[i].rm_eo-matched[i].rm_so;
  69.         char *ptr=&content[matched[i].rm_so];
  70.         strncpy(pos,ptr,len);
  71.         res.size+=(len+1);
  72.         pos[len]='\0';
  73.         res.child_num=i;
  74.         if(i>0){
  75.                 res.child[i-1]=pos;
  76.         }
  77.         pos+=(len+1);
  78.     }
  79.     res.rm_eo=matched[0].rm_eo;
  80.     return res;
  81. }//负责正则匹配的函数

  82. char *read_text(char *path){
  83.     FILE *fp;
  84.     char buff[MAX],*res=calloc(1,MAX);
  85.     if((fp=fopen(path,"r"))==NULL){
  86.             puts("读取文件失败!");
  87.         exit(-1);
  88.     }
  89.     while(!feof(fp)){
  90.             fgets(buff,MAX,fp);
  91.         strcat(res,buff);
  92.     }
  93.     fclose(fp);
  94.     return res;
  95. }//读文本文件

  96. int write_text(char path[],char *text){
  97.     FILE *fp;
  98.     if((fp=fopen(path,"w"))==NULL){
  99.             puts("打开文件失败!");
  100.         exit(-1);
  101.     }
  102.     int ret=fputs(text,fp);
  103.     fclose(fp);
  104.     return ret;
  105. }//写文本文件

  106. char *get_novel(int ch){
  107.     char *source=read_text("./tmp2");
  108.     struct Compile src=compile(1,"<div id=\"content\">(.*?)<.div>",source);
  109.     strrpc(src.child[0],"&nbsp;"," ");
  110.     strrpc(src.child[0],"<br />","\n");
  111.     char *res=malloc(1000000);
  112.     strcpy(res,chapters[ch].name);
  113.     strcat(res,"\n\n");
  114.     strcat(res,src.child[0]);
  115.     return res;
  116. }//负责提取tmp2中的小说正文,同时替换掉一些html标记

  117. void read_novel(int ch){
  118.     char command[200]="/usr/bin/wget -O tmp2  https://www.xbiquge6.com";
  119.     strcat(command,chapters[ch].url);
  120.     system(command);
  121.     char *aa=get_novel(ch);
  122.     write_text("tmp3",aa);
  123.     system("cat tmp3|less");
  124.     int isexist=-1;
  125.     if(ch>1){printf("        (0)、下一章");isexist=0;}
  126.     puts("        (1)、上一章        (2)、退出");
  127.     printf("阅读完毕,请选择下一步操作:");
  128.     int s=2;
  129.     LBA1: scanf("%d",&s);
  130.           switch(s){
  131.                   case 0:if(isexist==0) read_novel(ch-1);
  132.                 case 1:read_novel(ch+1);//这里不需break,因为这个函数有递无归。。。
  133.                 case 2:exit(EXIT_SUCCESS);
  134.                 default:printf("输入错误!请选择下一步操作:");
  135.                         goto LBA1;
  136.           }
  137. }//负责上下章节的切换

  138. int main(){
  139.     system("/usr/bin/wget -O tmp https://www.xbiquge6.com/9_9933/");
  140.     //system("/usr/bin/wget -O tmp https://www.xbiquge6.com/20_20331/");
  141.     char *content=read_text("./tmp");
  142.     //获取最新章节
  143.     struct Compile latest=compile(2,"最新章节:<a href=\"(.*)\" target=\"_blank\">(.*)</a></p>",content);
  144.     strcpy(chapters[0].name,latest.child[1]);
  145.     strcpy(chapters[0].url,latest.child[0]);
  146.     //获取最近章节
  147.     char *content_inverse=inverse(content);
  148.     for (int i=1;i<=6;i++){
  149.         struct Compile recent=compile(2,">dd/<>a/<(.{0,50})>\"(.{0,50})\"=ferh a<>dd<",content_inverse);
  150.         strcpy(chapters[i].name,inverse(recent.child[0]));
  151.         strcpy(chapters[i].url,inverse(recent.child[1]));
  152.         content_inverse+=recent.rm_eo;
  153.     }
  154.     puts("以下是最新章节:");
  155.     printf("        (0)、%s\n",chapters[0].name);
  156.     puts("以下是最近章节:");
  157.     for(int i=1;i<=6;i++){
  158.             printf("        (%d)、%s",i,chapters[i].name);
  159.         if(i%2==0){
  160.                 putchar('\n');
  161.         }
  162.     }
  163.     strrpc(chapters[1].url,"\" class=\"empty"," ");
  164.     printf("请输入章节序号(默认为0):");
  165.     int ch=0;
  166.     scanf("%d",&ch);
  167.     if (ch>6 ||ch<0){
  168.             puts("错误的输入!");
  169.         exit(EXIT_FAILURE);
  170.     }

  171.     read_novel(ch);
  172.     return 0;
  173. }
复制代码

对了,程序中用到了strrpc(),这是网友分享的函数,为了尊重版权,我将其另放在strrpc.h中了
如下:

  1. /* 功  能:将str字符串中的oldstr字符串替换为newstr字符串
  2. * 参  数:str:操作目标 oldstr:被替换者 newstr:替换者
  3. * 返回值:返回替换之后的字符串
  4. * 版  本: V0.2
  5. */
  6. char *strrpc(char *str,char *oldstr,char *newstr){
  7.     char bstr[strlen(str)];//转换缓冲区
  8.     memset(bstr,0,sizeof(bstr));

  9.     for(int i = 0;i < strlen(str);i++){
  10.         if(!strncmp(str+i,oldstr,strlen(oldstr))){//查找目标字符串
  11.             strcat(bstr,newstr);
  12.             i += strlen(oldstr) - 1;
  13.         }else{
  14.                 strncat(bstr,str + i,1);//保存一字节进缓冲区
  15.             }
  16.     }

  17.     strcpy(str,bstr);
  18.     return str;
  19. }

  20. /*
  21. ————————————————

  22. 版权声明:本文为CSDN博主「出奇」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。

  23. 原文链接:https://blog.csdn.net/qq_41673920/article/details/81390972
  24. */
复制代码

请注意:我用到了wget、cat、less等软件,所以。。。请在Linux系统下运行


小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2019-11-17 17:52:50 From FishC Mobile | 显示全部楼层
没有linux系统的话,可以使用coding的studio来运行代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2025-7-6 21:11

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表