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);
- 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);
}
- 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);
}