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(0, 5) != "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
最后祝大家端午节快乐!