|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
本帖最后由 cnjn 于 2019-11-17 17:31 编辑
写这个软件就是为了练习C语言下的正则表达式处理
主要解决了获取子匹配文本的获取,倒序正则匹配文本,以及。。。该死的内存泄漏
内存泄漏直接导致了我竟然无法使用fputs(),当我用gdb单步调试是fputs()崩溃的时候差点没把眼珠子瞪出来。。。它是库函数呐。。。它崩了我是没点办法诶后面才发现是自己写的代码太菜了。。。
内存泄漏是因为涉及到中文字符串的处理,strlen()求出来的长度似乎有点问题,,,所以,我用了一个非常笨的法子,就是一律malloc(1000000)。。。
下面贴出代码:- #include<stdio.h>
- #include<stdlib.h>
- #include<regex.h>
- #include<string.h>
- #include"strrpc.h"
- #define MAX 1000000
- /*
- 程序的思路:
- main函数调用wget保存章节目录的网页源码到tmp;
- main()调用compile()匹配出章节名及其url
- 打印出章节名,分为最新章节及最近章节
- 调用read_novel(),其中ch为chapters数组中的下标序号
- read_novel()调用wget获取章节正文的网页源码保存到tmp2
- read_novel()调用get_novel()提取出tmp2的内容并保存到tmp3
- read_novel()调用cat显示出处理好的章节
- read_novel()开始调用自身,也就是开始递归,以此实现上一章和下一章的切换,嗯。。。其实仔细观察就会发现这个函数是有递无归的(手动捂脸),因为我会强行直接退出软件
- 最最重要的是:我没有释放内存!我没有释放内存!我没有释放内存! 重要的事儿说三遍
- 大家千万不要学我,就是因此才会无法发现内存泄露,我被这个问题卡了5天呐!以后一定不敢只申请不释放了
- */
- struct Chapters{
- char name[100];
- char url[100];
- } chapters[7];//用来存储章节名及其url
- struct Compile{
- char *body;
- size_t size;
- int child_num;
- char **child;//嗯。。。这里应该是substring吧,就是子匹配文本
- regoff_t rm_eo;
- };//正则匹配的结果;body是匹配到的文本;child是子匹配文本数组,size是body+child的大小,包括所有的child。。。;rm_eo是匹配文本的结束下标,可用于多次匹配
-
- char *inverse(char *str){
- char *res=malloc(strlen(str)+1);
- int len=strlen("啊"),i=0,j=strlen(str)-1;
- while(str[i]!='\0'){
- if(str[i]>0x7F){
- strncpy(&res[j-len+1],&str[i],len);
- i+=len;
- j-=len;
-
- }else{
- strncpy(&res[j],&str[i],1);
- i++;
- j--;
- }
- }
- res[strlen(str)]='\0';
- return res;
- }//我自己写的一个字符串颠倒函数,因为网上分享的太不友好了。。。可处理中英文混合字符串,用于倒序正则匹配
- struct Compile compile(int child_num,char *pattern,char *content){
- //child_num代表子匹配文本的数量,pattern代表正则表达式,content表示被匹配文本
- regex_t preg;
- regcomp(&preg,pattern,REG_EXTENDED|REG_NEWLINE);
- regmatch_t matched[child_num+1];
- int vv=regexec(&preg,content,child_num+1,matched,REG_NOTBOL);
- if(vv!=0){
- char tmp[500];
- regerror(vv,&preg,tmp,500);
- puts(tmp);
- exit(-1);
- }
- struct Compile res;
- res.body=malloc(MAX);
- res.child=(char **)calloc(1,sizeof(char *));//这里child只分配了内存放指针,没有为具体的子匹配文本分配内存,因为body和child其实是共用一段内存的,中间用'\0'分开罢了
- res.size=0;
- res.child_num=0;
- char *pos=res.body;
- for (int i=0;i<=child_num;i++){
- int len=matched[i].rm_eo-matched[i].rm_so;
- char *ptr=&content[matched[i].rm_so];
- strncpy(pos,ptr,len);
- res.size+=(len+1);
- pos[len]='\0';
- res.child_num=i;
- if(i>0){
- res.child[i-1]=pos;
- }
- pos+=(len+1);
- }
- res.rm_eo=matched[0].rm_eo;
- return res;
- }//负责正则匹配的函数
- char *read_text(char *path){
- FILE *fp;
- char buff[MAX],*res=calloc(1,MAX);
- if((fp=fopen(path,"r"))==NULL){
- puts("读取文件失败!");
- exit(-1);
- }
- while(!feof(fp)){
- fgets(buff,MAX,fp);
- strcat(res,buff);
- }
- fclose(fp);
- return res;
- }//读文本文件
- int write_text(char path[],char *text){
- FILE *fp;
- if((fp=fopen(path,"w"))==NULL){
- puts("打开文件失败!");
- exit(-1);
- }
- int ret=fputs(text,fp);
- fclose(fp);
- return ret;
- }//写文本文件
- char *get_novel(int ch){
- char *source=read_text("./tmp2");
- struct Compile src=compile(1,"<div id=\"content\">(.*?)<.div>",source);
- strrpc(src.child[0]," "," ");
- strrpc(src.child[0],"<br />","\n");
- char *res=malloc(1000000);
- strcpy(res,chapters[ch].name);
- strcat(res,"\n\n");
- strcat(res,src.child[0]);
- return res;
- }//负责提取tmp2中的小说正文,同时替换掉一些html标记
- void read_novel(int ch){
- char command[200]="/usr/bin/wget -O tmp2 https://www.xbiquge6.com";
- strcat(command,chapters[ch].url);
- system(command);
- char *aa=get_novel(ch);
- write_text("tmp3",aa);
- system("cat tmp3|less");
- int isexist=-1;
- if(ch>1){printf(" (0)、下一章");isexist=0;}
- puts(" (1)、上一章 (2)、退出");
- printf("阅读完毕,请选择下一步操作:");
- int s=2;
- LBA1: scanf("%d",&s);
- switch(s){
- case 0:if(isexist==0) read_novel(ch-1);
- case 1:read_novel(ch+1);//这里不需break,因为这个函数有递无归。。。
- case 2:exit(EXIT_SUCCESS);
- default:printf("输入错误!请选择下一步操作:");
- goto LBA1;
- }
- }//负责上下章节的切换
- int main(){
- system("/usr/bin/wget -O tmp https://www.xbiquge6.com/9_9933/");
- //system("/usr/bin/wget -O tmp https://www.xbiquge6.com/20_20331/");
- char *content=read_text("./tmp");
- //获取最新章节
- struct Compile latest=compile(2,"最新章节:<a href=\"(.*)\" target=\"_blank\">(.*)</a></p>",content);
- strcpy(chapters[0].name,latest.child[1]);
- strcpy(chapters[0].url,latest.child[0]);
- //获取最近章节
- char *content_inverse=inverse(content);
- for (int i=1;i<=6;i++){
- struct Compile recent=compile(2,">dd/<>a/<(.{0,50})>\"(.{0,50})\"=ferh a<>dd<",content_inverse);
- strcpy(chapters[i].name,inverse(recent.child[0]));
- strcpy(chapters[i].url,inverse(recent.child[1]));
- content_inverse+=recent.rm_eo;
- }
- puts("以下是最新章节:");
- printf(" (0)、%s\n",chapters[0].name);
- puts("以下是最近章节:");
- for(int i=1;i<=6;i++){
- printf(" (%d)、%s",i,chapters[i].name);
- if(i%2==0){
- putchar('\n');
- }
- }
- strrpc(chapters[1].url,"\" class=\"empty"," ");
- printf("请输入章节序号(默认为0):");
- int ch=0;
- scanf("%d",&ch);
- if (ch>6 ||ch<0){
- puts("错误的输入!");
- exit(EXIT_FAILURE);
- }
- read_novel(ch);
- return 0;
- }
复制代码
对了,程序中用到了strrpc(),这是网友分享的函数,为了尊重版权,我将其另放在strrpc.h中了
如下:
- /* 功 能:将str字符串中的oldstr字符串替换为newstr字符串
- * 参 数:str:操作目标 oldstr:被替换者 newstr:替换者
- * 返回值:返回替换之后的字符串
- * 版 本: V0.2
- */
- char *strrpc(char *str,char *oldstr,char *newstr){
- char bstr[strlen(str)];//转换缓冲区
- memset(bstr,0,sizeof(bstr));
-
- for(int i = 0;i < strlen(str);i++){
- if(!strncmp(str+i,oldstr,strlen(oldstr))){//查找目标字符串
- strcat(bstr,newstr);
- i += strlen(oldstr) - 1;
- }else{
- strncat(bstr,str + i,1);//保存一字节进缓冲区
- }
- }
-
- strcpy(str,bstr);
- return str;
- }
- /*
- ————————————————
- 版权声明:本文为CSDN博主「出奇」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
- 原文链接:https://blog.csdn.net/qq_41673920/article/details/81390972
- */
复制代码
请注意:我用到了wget、cat、less等软件,所以。。。请在Linux系统下运行
|
|