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行,请看:
下面奉上一个截图,让大家看看提取小说的效果:
以上的代码大家不要从头开始读,要从最底下的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函数写成下面的样子:
这就用到了亲爱的右值引用,其实上面的代码可以不要std::move,因为regex_replace函数返回的就是一个右值。
不过可惜,上面的代码编译可以通过,但是程序运行的时候会报错。我很郁闷,也不知道为什么,希望高手指点。
我的代码是使用x64平台作为目标编译的,生成的程序在我的Windows 7 64位版本下运行良好。这进一步说明Boost库在64位的程序中使用很顺利。
友情提示一下,如果要把提取的小说保存到文件,只需要使用一个文件重定向即可,如下:
最后祝大家端午节快乐!
对于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
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"));
}
.
.
return std::move(regex_replace(output,patternToReplace,"\r\n"));
}
这就用到了亲爱的右值引用,其实上面的代码可以不要std::move,因为regex_replace函数返回的就是一个右值。
不过可惜,上面的代码编译可以通过,但是程序运行的时候会报错。我很郁闷,也不知道为什么,希望高手指点。
我的代码是使用x64平台作为目标编译的,生成的程序在我的Windows 7 64位版本下运行良好。这进一步说明Boost库在64位的程序中使用很顺利。
友情提示一下,如果要把提取的小说保存到文件,只需要使用一个文件重定向即可,如下:
getbookfromsina
39534
23601
23783
>
天使不在线.txt
最后祝大家端午节快乐!