• 【转】简单常识――关于stream


    • 简单常识――关于stream

    【博主按:本文以前网上流传的,最初出处已不详;写得精彩,特收录。不少C++老手都用不好std::stream,殊不知其功能是如此霸气。】

    • 从文件中读入一行

    简单,这样就行了:

    ifstream ifs("input.txt");
    char buf[1000];

    ifs.getline(buf, sizeof buf);

    string input(buf);

    当然,这样没有错,但是包含不必要的繁琐和拷贝,况且,如果一行超过1000个字符,就必须用一个循环和更麻烦的缓冲管理。下面这样岂不是更简单?

    string input;
    input.reserve(1000);
    ifstream ifs("input.txt");
    getline(ifs, input);

    不仅简单,而且安全,因为全局函数 getline 会帮你处理缓冲区用完之类的麻烦,如果你不希望空间分配发生的太频繁,只需要多 reserve 一点空间。

    这就是“简单常识”的含义,很多东西已经在那里,只是我一直没去用。

    • 一次把整个文件读入一个 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();

    • open 一个文件的那些选项

    ios::in     Open file for reading 
    ios::out    Open file for writing 
    ios::ate    Initial position: end of file 
    ios::app    Every output is appended at the end of file 
    ios::trunc  If the file already existed it is erased 
    ios::binary Binary mode

    • 还有 ios 的那些 flag

    FLAGEFFECT IF SET
    ios_base::boolalpha input/output bool objects as alphabetic names (truefalse).
    ios_base::dec input/output integer in decimal base format.
    ios_base::fixed output floating point values in fixed-point notation.
    ios_base::hex input/output integer in hexadecimal base format.
    ios_base::internal the output is filled at an internal point enlarging the output up to the field width.
    ios_base::left the output is filled at the end enlarging the output up to the field width.
    ios_base::oct input/output integer in octal base format.
    ios_base::right the output is filled at the beginning enlarging the output up to the field width.
    ios_base::scientific output floating-point values in scientific notation.
    ios_base::showbase output integer values preceded by the numeric base.
    ios_base::showpoint output floating-point values including always the decimal point.
    ios_base::showpos output non-negative numeric preceded by a plus sign (+).
    ios_base::skipws skip leading whitespaces on certain input operations.
    ios_base::unitbuf flush output after each inserting operation.
    ios_base::uppercase output uppercase letters replacing certain lowercase letters.

    There are also defined three other constants that can be used as masks:

    CONSTANTVALUE
    ios_base::adjustfield left | right | internal
    ios_base::basefield dec | oct | hex
    ios_base::floatfield scientific | fixed

     

    • 用我想要的分隔符来解析一个字符串,以及从流中读取数据

    这曾经是一个需要不少麻烦的话题,由于其常用而显得尤其麻烦,但是其实 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 代劳了。

    • 其它算法配合 iterator

    计算文件行数:

        int line_count = 
            count(istreambuf_iterator<char>(ifs.rdbuf()), 
                  istreambuf_iterator<char>(), 
                  ‘\n’);       

    当然确切地说,这是在计算文件中回车符的数量,同理,你也可以计算文件中任何字符的数量,或者某个 token 的数量:

        int token_count = 
            count(istream_iterator<string>(ifs), 
                  istream_iterator<string>(), 
                  "#include");       

    注意上面计算的是 “#include” 作为一个 token 的数量,如果它和其他的字符连起来,是不算数的。

    • Manipulator

    Manipulator 是什么?简单的说,就是一个接受一个 stream 作为参数,并且返回一个 stream 的函数,比如上面的 unskipws ,它的定义是这样的:

      inline ios_base& 
      noskipws(ios_base& __base) 
      { 
        __base.unsetf(ios_base::skipws); 
        return __base; 
      }

    这里它用了更通用的 ios_base 。知道了这一点,你大概不会对自己写一个 manipulator 有什么恐惧感了,下面这个无聊的 manipulator 会忽略 stream 遇到第一个分号之前所有的输入(包括那个分号):

    template <class charT, class traits>
    inline std::basic_istream<charT, traits>&
    ignoreToSemicolon (std::basic_istream<charT, traits>& s)
    {
        s.ignore(std::numeric_limits<int>::max(), s.widen(‘;’));
        return s;
    }

    不过注意,它不会忽略以后的分号,因为 ignore 只执行了一次。更通用一点,manipulator 也可以接受参数的,下面这个就是 ignoreToSemicolon 的通用版本,它接受一个参数, stream 会忽略遇到第一个该参数之前的所有输入,写起来稍微麻烦一点:

    struct IgnoreTo {
        char ignoreTo;
        IgnoreTo(char c) : ignoreTo(c) 
        {}
    };
        
    std::istream& operator >> (std::istream& s, const IgnoreTo& manip)
    {
        s.ignore(std::numeric_limits<int>::max(), s.widen(manip.ignoreTo)); 
        return s;
    }

    但是用法差不多:

        copy(istream_iterator<char>(ifs >> noskipws >> IgnoreTo(‘;’)), 
             istream_iterator<char>(), 
             ostream_iterator<char>(cout) 
        );

    其效果跟 IgnoreToSemicolon 一样。

  • 相关阅读:
    C#中 ()=>的含义
    大白话系列之C#委托与事件讲解(三)
    大白话系列之C#委托与事件讲解(二)
    C#委托
    php.ini
    mac 登陆phpmyadmin 提示 mysqli_real_connect(): (HY000/2002): No such file or directory
    mac 安装 mysql 5.7
    Mac下的PHP的配置与运行
    phpstorm 2019.1 mac
    激活 phpstorm2019.1 win10
  • 原文地址:https://www.cnblogs.com/maizhongfei/p/2439384.html
Copyright © 2020-2023  润新知