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

tinyXml2 -- https://github.com/leethomason/tinyxml2
rss      -- https://coolshell.cn/feed
         -- http://www.runoob.com/rss/rss-tutorial.html

正则表达式 进行过滤

参考接口:

struct RssItem
{
    string title;
    string link;
    string description;
    string content;
};

class RssReader
{
public:
    RssReader();
    void parseRss();//解析
    void dump(const string & filename);//输出
private:
    vector<RssItem> _rss;
 };   

要求:最后生成一个 pagelib.txt, 其格式:

<doc>
    <docid>1</docid>
    <title>...</title>
    <link>...</link>
    <description>...</description>
    <content>...</content>
</doc>
<doc>
    <docid>2</docid>
    <title>...</title>
    <link>...</link>
    <description>...</description>
    <content>...</content>
</doc>
<doc>
  ...
</doc>

RSS文件解析作业思路:xml -->rss-->tinyxml2(使用该库对rss文件进行解析)--> boost::regex/std::regex(使用正则表达式去除html标签)

提示:首先去读coolshell.xml文件,因为是一个rss文件,而我们需要找到rss的channel节点下面的item节点的title节点、link节点中间的文本,至于这些文本可以使用tinyxml2这个第三方库进行解析,所以这里需要看看timyxml2如何解析第三方库(可以看看timyxml2的源码),解析完成一个item后,可以将其存到vector中(也可以将这些原始信息经过后面正则表达式去除标签后再存起来),然后再去读第二个item(其实就是一个循环操作),其实第二个item是第一个item的兄弟节点(可以使用tinyxml2里面的函数进行跳转到第二个item),这样就可以解析coolshell.xml文档了。接着,因为description信息中包含html的标签,所以需要去除这样的html标签,如<p></p>,这个可以使用正则表达式,也就是std::regex进行去除,这个可以在cppreference.html中进行查找使用方法。

最后就是遍历vector,讲读取到的信息存到另外一个文件,格式需要自己定义,使用我们自定义的<doc> </doc>格式

date_11/13作业总结:

        wd老师已把作业实现思路讲的很清楚了,陌生的只是对RSS/HTML不了解,对第三方库tinyxml2的使用不了解,对正则表达式不了解。一开始觉得难,但是去了解了RSS/tinyxml2/正则表达式知识后,才发现,咦,原来不难啊,找到了处理html标签的正则表达式之后,作业基本完成-end.

1.了解RSS语言基本组成:RSS 教程 | 菜鸟教程

2.简单学习tinyxml2库,用于读取xml文件,用XMLElement类里面定义的函数可以获取RSS结点指针,tinyxml2库的安装和学习:参考:https://www.codenong.com/cs106726476/

1.复习:
    git clone https://github.com/leethomason/tinyxml2.git
    cd tinyxml2/
     make 
     ./xmltest
     sudo make install
2.把tinyxml2.cpp从github下载,放到自己的项目文件夹

3.简单学习XMLDocument类:
    1.XMLDocument xml;
     xml.LoadFile("coolshell.xml");//1.从当前文件夹读取xml文件

    2.XMLElement *category = xml.RootElement();//2.拿到根结点rss   

   3.XMLElement *channel = category->FirstChildElement("channel");//根节点的第一个孩子就是channel

   4.XMLElement *title = channel->FirstChildElement("title");//拿到channel的孩子结点title
   5.XMLElement *item = channel->FirstChildElement("item");//channel结点有很多个item子结点
    //作业的要求就是拿到item结点下的title,link,description,content,存入到结构体中,再用vector保存
    item = item->NextSiblingElement();//找下一个兄弟结点item

3.正则表达式处理网页html标签,C++Primer第五版简单了解了下语法,网上找去除html标签的正则表达式,拿来用即可。

1.正则表达式:regex reg("<[^>]*>");
2.regex_replace(str,reg,"")的使用。

代码实现:

01_RssHeader.h

#include <iostream>
#include <regex>
#include <fstream>
#include "tinyxml2.h"
#include <vector>
using namespace std;
using namespace tinyxml2;
#include <string>

struct RssItem
{
    string title;
    string link;
    string description;
    string content;
};
class RssReader
{
public:
    RssReader(){};

    void parseRss(const char *filename);//1.读取xml文件,解析相应内容,把每个结构体存入vector

    void read(); //2.把解析的内容输出到控制台

    void dump(const string & filename);//3.输出到文件

private:
    vector<RssItem> _rss;

};

02_Rss_main.cc

#include "01_RssHeader.h"


int main()
{
    RssReader s1;
    s1.parseRss("coolshell.xml");

    s1.dump("pagelib.dat");
    return 0;
}

03_parseData.cc

#include "01_RssHeader.h"

//获取Rss文件中channel结点下item结点的有用内容:title/link/descirption/content,保存到结构体vector
void RssReader::parseRss(const char *filename)//解析
{

    XMLDocument xml; //1.声明xml类,用来读取xml文件
    xml.LoadFile("coolshell.xml");//2.读取xml文件
    XMLElement *category = xml.RootElement();//3.拿到xml文件的根节点Rss
    XMLElement *channel = category->FirstChildElement("channel");//4.拿到channel结点指针
    XMLElement *title = channel->FirstChildElement("title");
    XMLElement *item = channel->FirstChildElement("item");//5.拿到item结点
    while(item)//6.设置循环,遍历channel下的每个item结点
    {
        RssItem rss_item;//7.把数据保存到结构体

        regex reg("<[^>]*>"); //8.正则表达式去除网页html标签

   		//9.获取title结点指针,把内容保存到结构体相应的变量
        XMLElement *item_title = item->FirstChildElement("title");//title结点
        rss_item.title = item_title->GetText(); //保存结点的内容
		
        //10.获取link结点指针,把内容保存到结构体相应的变量
        XMLElement *item_link = item->FirstChildElement("link");
        rss_item.link= item_link->GetText();
        //cout<<Rss.link<<endl;

		//11.获取desc结点指针,数据用正则去除html标签后,把内容保存到结构体相应的变量
        XMLElement *item_description = item->FirstChildElement("description");
        rss_item.description= item_description->GetText();

        rss_item.description = regex_replace(rss_item.description,reg,"");//去除html标签

        //11.获取content结点指针,数据用正则去除html标签后,把内容保存到结构体相应的变量
        XMLElement *item_content = item->FirstChildElement("content:encoded");
        rss_item.content= item_content->GetText();
        
        rss_item.content = regex_replace(rss_item.content,reg,"");//去除网页标签
        
        //12.把每个结构体保存进 vector,每个结构体都是一篇文章了
        _rss.push_back(rss_item);
        
        item = item->NextSiblingElement();//找下一个item结点
    }

}

void RssReader::read()//内容输出到控制台
{
    for(int i=0;i<_rss.size();i++)
    {
        cout<<_rss[i].title<<endl;
        cout<<_rss[i].link<<endl;
        cout<<_rss[i].description<<endl;
        cout<<_rss[i].content<<endl;
    }
}

//把内容按指定格式输出到指定文件
void RssReader::dump(const string & filename)
{
    ofstream ofs(filename);
    
    if(!ofs.good())
    {
        cerr<<"ofstream is not good!"<<endl;
        return;
    }


    for(int i=0;i<_rss.size();i++)
    {
        ofs<<"<doc>"<<"\n"
        <<"\t"<<"<docid>"<<i+1<<"</docid>"<<"\n"
        <<"\t"<<"<title>"<<_rss[i].title<<"</title>"<<"\n"

        <<"\t"<<"<link>"<<_rss[i].link<<"</link>"<<"\n"
        <<"\t"<<"<description>"<<_rss[i].description<<"</description>"<<"\n"
        <<"\t"<<"<content>"<<_rss[i].content<<"</content>"<<endl;
    }

    ofs.close();
}

Ubuntu下对代码进行编译:

g++ tinyxml2.cpp 01_RssHeader.h 02_Rss_main.cc 03_parseData.cc -o demo1 -std=c++11

部分输出结果:RSS文件来自Coolshell:Go编程模式 : 泛型编程 | 酷 壳 - CoolShell

<doc>
	<docid>1</docid>
	<title>Go编程模式 : 泛型编程</title>
	<link>https://coolshell.cn/articles/21615.html</link>
	<description>Go语言的1.17版本发布了,其中开始正式支持泛型了。虽然还有一些限制(比如,不能把泛型函数export),但是,可以体验了。我的这个《Go编程模式》的系列终于...
 Read More  Read More
The post Go编程模式 : 泛型编程 first appeared on 酷 壳 - CoolShell.</description>
	<content>Go语言的1.17版本发布了,其中开始正式支持泛型了。虽然还有一些限制(比如,不能把泛型函数export),但是,可以体验了。我的这个《Go编程模式》的系列终于有了真正的泛型编程了,再也不需要使用反射或是go generation这些难用的技术了。周末的时候,我把Go 1.17下载下来,然后,体验了一下泛型编程,还是很不错的。下面,就让我们来看一下Go的泛型编程。(注:不过,如果你对泛型编程的重要性还不是很了解的话,你可以先看一下之前的这篇文章《Go编程模式:Go Generation》,然后再读一下《Go编程模式:MapReduce》)
本文是全系列中第10 / 10篇:Go编程模式Go编程模式:切片,接口,时间和性能Go 编程模式:错误处理Go 编程模式:Functional OptionsGo编程模式:委托和反转控制Go编程模式:Map-ReduceGo 编程模式:Go GenerationGo编程模式:修饰器Go编程模式:PipelineGo 编程模式:k8s Visitor 模式Go编程模式 : 泛型编程&laquo; 上一篇文章
初探
我们先来看一个简单的示例:

package main

import "fmt"

func print[T any] (arr []T) {
  for _, v := range arr {
    fmt.Print(v)
    fmt.Print(" ")
  }
  fmt.Println("")
}

func main() {
  strs := []string{"Hello", "World",  "Generics"}
  decs := []float64{3.14, 1.14, 1.618, 2.718 }
  nums := []int{2,4,6,8}

  print(strs)
  print(decs)
  print(nums)
}
上面这个例子中,有一个 print() 函数,这个函数就是想输出数组的值,如果没有泛型的话,这个函数需要写出 int 版,float版,string 版,以及我们的自定义类型(struct)的版本。现在好了,有了泛型的支持后,我们可以使用 [T any] 这样的方式来声明一个泛型类型(有点像C++的 typename T),然后面都使用 T 来声明变量就好。
上面这个示例中,我们泛型的 print() 支持了三种类型的适配—— int型,float64型,和 string型。要让这段程序跑起来需要在编译行上加上 -gcflags=-G=3编译参数(这个编译参数会在1.18版上成为默认参数),如下所示:
$ go run -gcflags=-G=3 ./main.go
有了个操作以后,我们就可以写一些标准的算法了,比如,一个查找的算法
func find[T comparable] (arr []T, elem T) int {
  for i, v := range arr {
    if  v == elem {
      return i
    }
  }
  return -1
}
我们注意到,我们没有使用 [T any]的形式,而是使用 [T comparable]的形式,comparable是一个接口类型,其约束了我们的类型需要支持 == 的操作, 不然就会有类型不对的编译错误。上面的这个 find() 函数同样可以使用于 int, float64或是string类型。
从上面的这两个小程序来看,Go语言的泛型已基本可用了,只不过,还有三个问题:

一个是 fmt.Printf()中的泛型类型是 %v 还不够好,不能像c++ iostream重载 &gt;&gt; 来获得程序自定义的输出。
另外一个是,go不支持操作符重载,所以,你也很难在泛型算法中使用“泛型操作符”如:== 等
最后一个是,上面的 find() 算法依赖于“数组”,对于hash-table、tree、graph、link等数据结构还要重写。也就是说,没有一个像C++ STL那样的一个泛型迭代器(这其中的一部分工作当然也需要通过重载操作符(如:++ 来实现)

不过,这个已经很好了,让我们来看一下,可以干哪些事了。
数据结构
Stack 栈
编程支持泛型最大的优势就是可以实现类型无关的数据结构了。下面,我们用Slices这个结构体来实现一个Stack的数结构。
首先,我们可以定义一个泛型的Stack
type stack [T any] []T
看上去很简单,还是 [T any] ,然后 []T 就是一个数组,接下来就是实现这个数据结构的各种方法了。下面的代码实现了 push() ,pop(),top(),len(),print()这几个方法,这几个方法和 C++的STL中的 Stack很类似。(注:目前Go的泛型函数不支持 export,所以只能使用第一个字符是小写的函数名)
func (s *stack[T]) push(elem T) {
  *s = append(*s, elem)
}

func (s *stack[T]) pop() {
  if len(*s) &gt; 0 {
    *s = (*s)[:len(*s)-1]
  } 
}
......

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐