海边沫沫

相濡以沫,不如相忘于江湖
posts - 9, comments - 113, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理
Visual Studio 2010发布有几个月了,但是中文版一直到5月底才有。软件一拿到手,我就迫不及待地安装,想一睹Visual C++ 2010的风采。

对于Visual C++,我比较关心的几个问题有:一、对新的C++ 0x支持如何?二、能否顺利编译和使用Boost?说到底,这两个问题差不多是一个问题,因为大家都知道Boost和C++0x的关系,C++0x中的很多特性,其实都是从Boost中来的。

首先来说Visual C++对C++0x的新特性的支持,主要有四点:一、auto关键字的新意义,有了该特性,我们就不用再在使用STL 的iterator时写一长串代码了,编译器可以自动推断其类型;二、static_assert关键字,该特性不稀奇,Boost里面早就有了;三、右值引用,这个很好,主要解决了移动语意和完美转发的问题,在Visual Studio的欢迎页中链接了一篇文档就是讲的这个东西,还有一篇文档讲了我们在设计类时,怎么编写移动构造函数和移动赋值运算符,值得一看。(这里的移动指的是move,和copy相对,可不是指中国移动哦)四、lambda表达式,这个也是Boost中早就有的功能。

毕竟C++0x标准还没有发布,Visual C++支持到这一步,已经很不错了,还差的几个方面是:Concepts、可变模板参数、多线程内存模型。。。说实话,具体还有多少我也说不清楚。

至于纳入Visual C++的标准库的东西,还是只有tr1,这在Visual Studio 2008时代已经有了,没什么新意。要想找激情,还是去深度探索Boost吧。安装好Visual Studio 2010后,我马上就下载了最近的Boost,按照文档的说明进行安装,安装非常的顺利。

为了使用Visual C++ 2010和Boost,我给自己找了点事,那就是编程去提取新浪读书频道上的小说。程序进行得很顺利,只有区区150行,请看:
  1 #include <iostream>
  2 #include <string>
  3 #include <boost\lexical_cast.hpp>
  4 #include <boost\asio.hpp>
  5 #include <boost\regex.hpp>
  6 
  7 using namespace std;
  8 using namespace boost;
  9 
 10 string clearUp(stringstream& input){
 11     /*
 12     下面的代码使用boost::regex库进行字符串的替换
 13     */
 14     regex patternOfTitle("(.*)<h1>(.*)</h1>(.*)");
 15     regex patternOfBody("(.*)<div id=\"contTxt\" class=\"contTxt1\"><p>(.*)</p></div>(.*)");
 16 
 17     string line;
 18     string output;
 19     smatch results;
 20     int status = 0;
 21     while(getline(input,line)){
 22         if(status == 0){//还没有碰到标题
 23             if(regex_match(line,results,patternOfTitle)){
 24                 output += results[2];
 25                 output += "\r\n";
 26                 status = 1//处理完标题
 27             }
 28         }else{//处理正文
 29             if(regex_match(line,results,patternOfBody)){
 30                 output += results[2];
 31                 status = 2
 32                 break;
 33             }
 34         }
 35     }
 36     //将output中的</p><p>替换成回车换行符
 37     regex patternToReplace("</p><p>");
 38     return regex_replace(output,patternToReplace,"\r\n");
 39 }
 40 
 41 void getChapter(ostream& ostream,vector<int>& args){
 42     /*
 43     以下代码使用boost::asio库
 44     具体用法请参考boost文档
 45     */
 46     asio::io_service io_service;
 47     asio::ip::tcp::resolver resolver(io_service);
 48     asio::ip::tcp::resolver::query query("vip.book.sina.com.cn","http");
 49     asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve(query);
 50     asio::ip::tcp::resolver::iterator end;
 51     system::error_code error = asio::error::host_not_found;
 52     asio::ip::tcp::socket socket(io_service);
 53     while (error && endpoint_iterator != end)
 54     {
 55       socket.close();
 56       socket.connect(*endpoint_iterator++, error);
 57     }
 58     if (error)
 59       throw system::system_error(error);
 60 
 61     asio::streambuf request;
 62     std::ostream request_stream(&request);
 63     
 64     request_stream << "GET " << "/book/chapter_"<< args[0<< "_" << args[1<< ".html" << " HTTP/1.0\r\n";
 65     request_stream << "Host: " << "vip.book.sina.com.cn" << "\r\n";
 66     request_stream << "Accept: */*\r\n";
 67     request_stream << "Connection: close\r\n\r\n";
 68 
 69     asio::write(socket, request);
 70 
 71     asio::streambuf response;
 72     asio::read_until(socket, response, "\r\n");
 73 
 74     std::istream response_stream(&response);
 75     string http_version;
 76     response_stream >> http_version;
 77     unsigned int status_code;
 78     response_stream >> status_code;
 79     string status_message;
 80     getline(response_stream, status_message);
 81     if (!response_stream || http_version.substr(05!= "HTTP/")
 82     {
 83       cout << "Invalid response\n";
 84       throw system::system_error(error);
 85     }
 86     if (status_code != 200)
 87     {
 88       ostream << "Response returned with status code " << status_code << "\n";
 89       throw system::system_error(error);
 90     }
 91 
 92     // 读取数据,并把数据放入一个string中,这里用到std::stringstream
 93     stringstream content;
 94     while (asio::read(socket, response,
 95           asio::transfer_at_least(1), error))
 96       content << & response;
 97     if (error != asio::error::eof)
 98       throw system::system_error(error);
 99 
100     //调用clearUp函数,从杂乱的HTML文件中提取纯文本的小说
101     ostream << clearUp(content);
102 }
103 
104 void getBook(ostream& ostream,vector<int>& args){
105     /*
106     如果没有到最后一章,则执行循环
107     以下载所有章节
108     */
109     while(args[1<= args[2]){
110         getChapter(ostream,args);
111         args[1]++;
112     }
113 }
114 
115 int _tmain(int argc, _TCHAR* argv[])
116 {
117     /*
118     新浪读书频道的URL地址为“http://vip.book.sina.com.cn/book/chapter_120954_83221.html”的形式
119     其中的两个数字一个代表书的ID,一个代表章节的ID
120     所以我们的程序需要接受的参数有三个,分别为书的ID,第一章的ID和最后一章的ID,该程序自动下载从
121     第一章到最后一章的内容,并整理
122     该程序的使用方法为:
123     GetBookFromSina bookId firstChapterId lastChapterId
124     */
125 
126     /*
127     下面的程序片段使用lexical_cast库来将命令行输入的参数转换为整数
128     */
129     if(argc != 4){
130         cout << "输入不正确!正确的输入为:GetBookFromSina bookId firstChapterId lastChapterId" << endl;
131         return 0;
132     }
133     vector<int> args;
134     try{
135         for(int i=1; i < 4; i++){
136             args.push_back(lexical_cast<int>(argv[i]));
137         }
138     }catch(bad_lexical_cast &){
139         cout << "输入不正确!正确的输入为:GetBookFromSina bookId firstChapterId lastChapterId" << endl;
140         cout << "请确定输入的参数为整数" << endl;
141         return 0;
142     }
143 
144     /*
145     调用getBook函数,输入一个ostream &类型的参数和一个vector<int>&参数
146     如果输入的是cout,则输出到控制台
147     也可以把输出流输出到文件或字符串,只需要传入不同的参数即可
148 
149     之所以不让getBook函数返回字符串,而是接受一个流对象作为参数,是因为getBook中有一个循环
150     如果要返回字符串的话,需要把很多字符串连接成一个更大的字符串,影响效率
151     */
152     try{
153         getBook(cout,args);
154     }catch(system::system_error&){
155         cout << "获取文章的过程中发生错误" << endl;
156     }
157 }
158 

下面奉上一个截图,让大家看看提取小说的效果:
 

以上的代码大家不要从头开始读,要从最底下的main函数一个一个往上读。在main函数中,使用了Boost的lexical_cast将命令行的参数从字符串转化成int,如果输入的参数非法,程序就会退出。在main中调用getBook函数,也许大家觉得给getBook函数传入一个cout流对象不妥,侵入性太大,但是这确实最有效率的办法,因为如果让getBook函数返回字符串的话,那将是一个非常大的字符串,而且要在getBook里面讲一百多个字符串组装成这个大字符串,效率很低。在getBook中调用getChapter函数,在getChapter函数中使用Boost的ASIO库,从网络上读取数据后,再调用clearUp函数进行处理,在clearUp函数中,使用了Boost的Regex库。

在这个程序中,因为要不断地对字符串进行处理,所以内存的分配和效率方面就需要特别注意,我做了一些努力以尽量减少字符串的复制,但是有两个地方还是做得不过好。一个地方是从response中把数据读入到了一个stringstream中,这里发生了一次复制,response是一个streambuffer对象,如果能直接从response对象中提取数据进行处理就更好了,但是问题是,streambuffer中数据是从网络中读取的,而从网络读取数据时一次传输多少谁也不知道,其中的数据不一定是完整的,所以程序中用了一个循环。当然,肯定有办法让response中尽量包含完整的数据,只不过要更耐心地去读ASIO的文档。(我的程序使用ASIO的部分是直接从Boost ASIO的示例代码抄的,没有仔细读文档。)

另外一个不好的地方就是clearUp函数,该函数返回的时候,发生了一次字符串复制。(其实编译器会优化掉)
如果不考虑编译器优化的话,最好的办法是把clearUp函数写成下面的样子:
string&& clearUp(stringstream& input){
.
.
return std::move(regex_replace(output,patternToReplace,"\r\n"));
}

这就用到了亲爱的右值引用,其实上面的代码可以不要std::move,因为regex_replace函数返回的就是一个右值。

不过可惜,上面的代码编译可以通过,但是程序运行的时候会报错。我很郁闷,也不知道为什么,希望高手指点。

我的代码是使用x64平台作为目标编译的,生成的程序在我的Windows 7 64位版本下运行良好。这进一步说明Boost库在64位的程序中使用很顺利。

友情提示一下,如果要把提取的小说保存到文件,只需要使用一个文件重定向即可,如下:
getbookfromsina 39534 23601 23783 > 天使不在线.txt

最后祝大家端午节快乐!

Feedback

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-12 21:55 by 空明流转
Concept已经不是0x的内容了。
我建议楼主了解一下新的0x的标准内容。

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-12 22:09 by 海边沫沫
@空明流转
谢谢指正!
不是我不想了解,而是有很多东西我看不懂。我文章中列出的,是我稍微看得懂一点点的,呵呵。
对于C++0x的了解,我主要是看的刘未鹏的博客,还有一些通过搜索引擎搜到的零散碎片。
你能给我一些更好的资料吗?

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-13 08:48 by 欣萌
学习了。

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-13 11:26 by 空明流转
http://www.open-std.org/jtc1/sc22/wg21/
这里是C++0x标准工作组的官方网站,你可以找到任意的更新。
http://gcc.gnu.org/projects/cxx0x.html
这里是GCC对新标准Support的程度参考。

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-13 14:17 by 莫失莫忘
我对剧情也比较好奇,那个一类人是什么一类人

# re: 一个工具、一个库和一部小说  回复  更多评论   

2010-06-15 13:45 by linnet
关注

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理