QQ空间互踩 | 代码交流官方QQ群: ①4164128 ②13937921网络证件中心 | 更多QQ空间日志编辑器 | 火星文字
发新话题
打印

[〖其他语言交流专区〗] C++ Advanced STL Programming (4)

本主题由 花少 于 2009-1-6 10:02 移动

C++ Advanced STL Programming (4)

一次把整个文件读入一个 string
我希望你的答案不要是这样:
string input;
while( !ifs.eof() )
{
    string line;
    getline(ifs, line);
    input.append(line).append(1, '\n');
}
当然了,没有错,它能工作,但是下面的办法是不是更加符合 C++ 的精神呢?
string input(
    istreambuf_iterator<char>(instream.rdbuf()),
    istreambuf_iterator<char>()
);
同样,事先分配空间对于性能可能有潜在的好处:
string input;
input.reserve(10000);
input.assign(
    istreambuf_iterator<char>(ifs.rdbuf()),
    istreambuf_iterator<char>()
);
很简单,不是么?但是这些却是我们经常忽略的事实。
补充一下,这样干是有问题的:
    string input;
    input.assign(
        istream_iterator<char>(ifs),
        istream_iterator<char>()
    );
因为它会忽略所有的分隔符,你会得到一个纯“字符”的字符串。最后,如果你只是想把一个文件的内容读到另一个流,那没有比这更快的了:
    fstream fs("temp.txt");
    cout << fs.rdbuf();
因此,如果你要手工 copy 文件,这是最好的(如果不用操作系统的 API):
   ifstream ifs("in.txt");
   ofstream ofs("out.txt");
   ofs << in.rdbuf();
=====================================================
用我想要的分隔符来解析一个字符串,以及从流中读取数据
这曾经是一个需要不少麻烦的话题,由于其常用而显得尤其麻烦,但是其实 getline 可以做得不错:
    getline(cin, s, ';');   
    while ( s != "quit" )
    {
        cout << s << endl;
        getline(cin, s, ';');
    }
简单吧?不过注意,由于这个时候 getline 只把 ; 作为分隔符,所以你需要用 ;quit; 来结束输入,否则 getline 会把前后的空格和回车都读入 s ,当然,这个问题可以在代码里面解决。
同样,对于简单的字符串解析,我们是不大需要动用什么 Tokenizer 之类的东西了:
#include <iostream>
#include <sstream>
#include <string>

using namespace std;

int main()
{
    string s("hello,world, this is a sentence; and a word, end.");
    stringstream ss(s);
   
    for ( ; ; )
    {
        string token;
        getline(ss, token, ',');
        if ( ss.fail() ) break;
        
        cout << token << endl;
    }
}
输出:
hello
world
this is a sentence; and a word
end.
很漂亮不是么?不过这么干的缺陷在于,只有一个字符可以作为分隔符。
=====================================================
把原本输出到屏幕的东西输出到文件,不用到处去把 cout 改成 fs
#include <iostream>
#include <fstream>
using namespace std;
int main()
{     
    ofstream outf("out.txt");  
    streambuf *strm_buf=cout.rdbuf();     
    cout.rdbuf(outf.rdbuf());  
    cout<<"write something to file"<<endl;  
    cout.rdbuf(strm_buf);   //recover  
    cout<<"display something on screen"<<endl;
    system("PAUSE");
    return 0;
}

输出到屏幕的是:
display something on screen
输出到文件的是:
write something to file
也就是说,只要改变 ostream 的 rdbuf ,就可以重定向了,但是这招对 fstream 和 stringstream 都没用。
=====================================================
关于 istream_iterator ostream_iterator
经典的 ostream_iterator 例子,就是用 copy 来输出:
#include <iostream>

#include <fstream>


#include <sstream>


#include <algorithm>


#include <vector>


#include <iterator>



using namespace std;



int main()


{   
    vector<int> vect;


    for ( int i = 1; i <= 9; ++i )


        vect.push_back(i);


        
    copy(vect.begin(), vect.end(),


        ostream_iterator<int>(cout, " ")


    );


    cout << endl;


   
    ostream_iterator<double> os_iter(cout, " ~ ");


    *os_iter = 1.0;


    os_iter++;


    *os_iter = 2.0;


    *os_iter = 3.0;


}


输出:
1 2 3 4 5 6 7 8 9

1 ~ 2 ~ 3 ~

很明显,ostream_iterator 的作用就是允许对 stream iterator 的操作,从而让算法可以施加于 stream 之上,这也是 STL 的精华。与前面的读取文件相结合,我们得到了显示一个文件最方便的办法:
    copy(istreambuf_iterator<char>(ifs.rdbuf()),
         istreambuf_iterator<char>(),
         ostreambuf_iterator<char>(cout)


    );

同样,如果你用下面的语句,得到的会是没有分隔符的输出:
    copy(istream_iterator<char>(ifs),

         istream_iterator<char>(),


         ostream_iterator<char>(cout)


    );

那多半不是你要的结果。如果你硬是想用 istream_iterator 而不是 istreambuf_iterator 呢?还是有办法:
    copy(istream_iterator<char>(ifs >> noskipws),

         istream_iterator<char>(),


         ostream_iterator<char>(cout)


    );

但是这样不是推荐方法,它的效率比第一种低不少。
如果一个文件 temp.txt 的内容是下面这样,那么我的这个从文件中把数据读入 vector 的方法应该会让你印象深刻。
12345 234 567
89    10

程序:
#include <iostream>

#include <fstream>


#include <algorithm>


#include <vector>


#include <iterator>



using namespace std;



int main()


{   
    ifstream ifs("temp.txt");


   
    vector<int> vect;


    vect.assign(istream_iterator<int>(ifs),
        istream_iterator<int>()
    );



    copy(vect.begin(), vect.end(), ostream_iterator<int>(cout, " "));
}

输出:
12345 234 567 89 10
很酷不是么?判断文件结束、移动文件指针之类的苦工都有 istream_iterator 代劳了。
=====================================================

        

TOP

做梦

提示: 作者被禁止或删除 内容自动屏蔽

TOP

提示: 作者被禁止或删除 内容自动屏蔽

TOP

发新话题