鱼C论坛

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

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

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

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

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

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系统下运行


想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2019-11-17 17:52:50 From FishC Mobile | 显示全部楼层
没有linux系统的话,可以使用coding的studio来运行代码
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-1-16 13:32

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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