小说追更器——regex库函数的练习
本帖最后由 cnjn 于 2019-11-17 17:31 编辑写这个软件就是为了练习C语言下的正则表达式处理
主要解决了获取子匹配文本的获取,倒序正则匹配文本,以及。。。该死的内存泄漏{:9_220:}
内存泄漏直接导致了我竟然无法使用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;
char url;
} chapters;//用来存储章节名及其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!='\0'){
if(str>0x7F){
strncpy(&res,&str,len);
i+=len;
j-=len;
}else{
strncpy(&res,&str,1);
i++;
j--;
}
}
res='\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;
int vv=regexec(&preg,content,child_num+1,matched,REG_NOTBOL);
if(vv!=0){
char tmp;
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.rm_eo-matched.rm_so;
char *ptr=&content.rm_so];
strncpy(pos,ptr,len);
res.size+=(len+1);
pos='\0';
res.child_num=i;
if(i>0){
res.child=pos;
}
pos+=(len+1);
}
res.rm_eo=matched.rm_eo;
return res;
}//负责正则匹配的函数
char *read_text(char *path){
FILE *fp;
char buff,*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," "," ");
strrpc(src.child,"<br />","\n");
char *res=malloc(1000000);
strcpy(res,chapters.name);
strcat(res,"\n\n");
strcat(res,src.child);
return res;
}//负责提取tmp2中的小说正文,同时替换掉一些html标记
void read_novel(int ch){
char command="/usr/bin/wget -O tmp2https://www.xbiquge6.com";
strcat(command,chapters.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.name,latest.child);
strcpy(chapters.url,latest.child);
//获取最近章节
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.name,inverse(recent.child));
strcpy(chapters.url,inverse(recent.child));
content_inverse+=recent.rm_eo;
}
puts("以下是最新章节:");
printf(" (0)、%s\n",chapters.name);
puts("以下是最近章节:");
for(int i=1;i<=6;i++){
printf(" (%d)、%s",i,chapters.name);
if(i%2==0){
putchar('\n');
}
}
strrpc(chapters.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;//转换缓冲区
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系统下运行
没有linux系统的话,可以使用coding的studio来运行代码
页:
[1]