C++ day10 读取xml文件,过滤取出注释

1.要求:使用tinyXml2解析RSS文件,并生成一个网页库pagelib.dat

2.具体步骤:

1.用tinyxml2取description、link、title、content属性的值,用re过滤,数据保存到结构体,new结构体指针,将指针存到容器中

2.把容器的指针取出来,取数据保存到新文件

3.包含的文件

(1)测试文件 testForXml.cpp

(2)头文件 tiny,h tinyxml2.h

(3)具体函数实现文件 tinyFunc.cpp tinyxml2.cpp(库文件的cpp)

4.实现的效果展示
在这里插入图片描述

5.具体代码

1)tiny,h 头文件

2)testForXml.cpp 测试文件

3)tinyFunc.cpp 具体函数实现

1)tiny,h 头文件

#pragma once
#include <vector>
#include <string>
#include <iostream>
#include "tinyxml2.h"
#include <regex>
#include <fstream>
using namespace tinyxml2;
using namespace std;
using std::cout;
using std::endl;
using std::string;
using std::vector;
using std::to_string;


//存在容器的结构体形式
struct RssItem
{
    size_t id;
    string title;
    string link;
    string description;
    string content_klx;
};

//用来解析和输出xml的类
class RssReader
{
private:
    //从xml提取出信息存到容器,至于为什么要放到private,你放到public也行其实,  -。-
    string find_klx_Info(XMLElement* node,string str,regex re);
    //容器要存结构体指针,为什么要存指针,而不是结构体,答:一开始考虑要迭代器遍历,你要直接存结构体也行
    vector<RssItem*> _rss;
    //用来存访问的文件名
    string _filename;
public:
    RssReader();
    //按照文件名初始化
    RssReader(const string&filename);
    ~RssReader();

    //解析xml文件,解析出来数据再用regex清洗数据后放到容器
    void parseRss();

    //将容器的信息保存到文件里面
    void saveFile_klx(string& filename);
};

//将结构体里面的内容取出来ele的子节点eleChild上,然后子结点插到ele节点上
void setElement(XMLDocument& doc, XMLElement* ele, string str,const string&text);
  1. testForXml.cpp 测试文件
#include "tiny.h"

//包装成一个要测试的函数
void mainTest();
int main()
{
    mainTest();
    return 0;
}

void mainTest()
{
    //1.先用类建个对象,加载文件
    string filename="coolshell.xml";
    RssReader sp1(filename);

    //2.用这个对象开始解析xml文件,解析出来数据再用regex清洗数据后放到容器
    sp1.parseRss();
    //3.容器的数据存到data.txt
    filename="data.txt";
    sp1.saveFile_klx(filename);
}
  1. tinyFunc.cpp 具体函数实现
#include "tiny.h"
//下面主要是函数的实现
//按头文件的顺序来


//从xml提取出信息存到容器
string RssReader::find_klx_Info(XMLElement* node,string str,regex re)
{
    //资料来源
    //https://blog.csdn.net/qq_34802416/article/details/79307102?utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromMachineLearnPai2%7Edefault-1.control
    string temp(node->FirstChildElement(str.c_str())->GetText());
    //使用 std::smatch 可以方便的对匹配的结果进行获取,全文匹配,即要求整个字符串符合匹配规则
    std::smatch match;

    string res;
    //若传入的字符串是content:encoded的内容,因为这个内容很多,而且很多是分段的,需要特别处理
    if(str == "content:encoded")
    {
        //迭代器声明
        string::const_iterator iterStart = temp.begin();
        string::const_iterator iterEnd = temp.end();
        //search是搜索匹配,即搜索字符串中存在符合规则的子字符串
        //search找到了之后就用match赋给字符串res,match存着找到的结果
        while(regex_search(iterStart,iterEnd,match,re)){
            //不断更新到字符串里面
            res += match.str(1);//或是match[0]
            //更新搜索位置到开始的地方,搜索剩下的字符
            iterStart = match[0].second;
        }
        return res;
    }
    //若传入的字符串不是content:encoded,普通的一找就找到全部的了,直接返回字符串
    else{
        std::regex_search(temp,match,re);
        return match.str(1);
    }
}

//构造函数,没用的函数,不写也行
RssReader::RssReader()
{
    cout<<"老子开使用RssReader"<<endl;
}

//文件名初始化,重载构造函数,存文件名到private中
RssReader::RssReader(const string&filename)
: _filename(filename)
{
    std::cout<<"老子开始使用RssReader"<<std::endl;
}

//析构函数,由于容器存都是new出来的结构体指针,需要后续释放掉
RssReader::~RssReader()
{
    //定义迭代器
    vector<RssItem*>::iterator iter;
    for(iter = _rss.begin();iter!=_rss.end();++iter){
        delete (*iter);
        *iter = nullptr;
    }
    cout<<"老子RssReader干的活结束了,饮茶去了"<<endl;
}

//用来解析xml文件,按二叉树遍历xml
void RssReader::parseRss()
{
    //1.判断文件名是否为空
    if(!_filename.size()){
        std::cerr<<"文件名是空的"<<std::endl;
        return;
    }

    //2.加载xml文件,初始化,判断是否打开文件失败
    XMLDocument doc;
    int ret = doc.LoadFile(_filename.c_str());
    //返回值0表示成功,若返回不等于0表示失败打印后直接返回
    if(0!=ret)
    {
        std::cerr<< "打开xml文件失败" <<std::endl;
        return;
    }

    //3.初始化根节点,开始遍历节点
    XMLElement* root = doc.FirstChildElement("rss")->FirstChildElement("channel");
    XMLElement *node=root->FirstChildElement("item");
    size_t id = 0;
    regex re;
    while(NULL!=node)
    {
        //新建结构体指针-》指向结构体
        RssItem* tmp_klx=new RssItem();
        ++id;
        tmp_klx->id=id;
        //利用正则表达式摘取有用信息
        //(.*)会尽量匹配尽可能多的句子,单个字符匹配任意次,即贪婪匹配
        //(.*?)尽可能匹配少的句子,有时候会是一个词满足条件的情况只匹配一次,即懒惰匹配
        //详细资料参考python网络爬虫从入门到实践的(第二版)的5.1章讲解re的匹配问题
        re = "(.*)";
        //把title节点下的(.*)也就是一整句话都拷贝出来放在结构体
        tmp_klx->title = find_klx_Info(node,"title",re);
        //把title节点下的(.*)也就是一整句话都拷贝出来放在结构体
        tmp_klx->link = find_klx_Info(node,"link",re);

        //这里的<p>和</p>特定指向两个中间的内容
        re = "<p>(.*?)</p>";
        tmp_klx->description = find_klx_Info(node,"description",re);

        //下面的\\"转义为 "\"
        // \s表示匹配空白字符,\S表示匹配任何非空白字符,[]表示一组字符
        // *表示匹配前一个字符0次或n次
        //  ?表示匹配前一个字符0次或1次
        //中文注释都是在 >  <两个符号之间
        re = ">([\\s\\S]*?)<";
        tmp_klx->content_klx = find_klx_Info(node,"content:encoded",re); 
        //容器存结构体指针
        _rss.push_back(tmp_klx);
        node = node->NextSiblingElement();
    }
}


//输出到.txt文件
void RssReader::saveFile_klx(string&filename)
{
    //1.若容器为空,打印错误并返回
    if(!_rss.size()){
        std::cerr<<"容器是空的大兄弟,你搁着整啥呢,一天天的"<<std::endl;
        return;
    }

    //2.初始化迭代器开始遍历
    XMLDocument doc;
    vector<RssItem*>::iterator iter;
    //用迭代器遍历容器,遍历结构体指针,也就是遍历结构体的内容,放到XML指定的节点推到doc
    for(iter = _rss.begin();iter != _rss.end();++iter){
        //每一个结构体的内容
        //先写doc
        XMLElement *ele = doc.NewElement("doc");
        //后写docid
        setElement(doc,ele,"docid",to_string((*iter)->id));
        //写title
        setElement(doc,ele,"title",(*iter)->title);
        setElement(doc,ele,"link",(*iter)->link);
        setElement(doc,ele,"description",(*iter)->description);
        setElement(doc,ele,"content",(*iter)->content_klx);
        //尾插进去
        doc.InsertEndChild(ele);
    }
    //注意这个saveFile是第三方库里面的,不是自己写的
    //把doc保存到既定的文件
    doc.SaveFile(filename.c_str());
}

//将结构体里面的内容取出来ele的子节点eleChild上,然后子结点插到ele节点上
void setElement(XMLDocument &doc,XMLElement* ele,string str,const string&text){
    XMLElement *eleChild = doc.NewElement(str.c_str());
    eleChild->SetText(text.c_str());
    ele->InsertEndChild(eleChild);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值