C++ IO Streams 学习笔记

迫于拙劣的cpp水平,来补补以前忽略掉的cpp细节。

老规矩,先放资料。

参考资料:

A Gentle Introduction to C++ IO Streams

"Designing and implementing a general input/output facility for a programming language is notoriously difficult" - Bjarne Stroustrup

Stream的基本认识

说说我的理解。stream(流)可以看做输入输出的抽象。我们通过流可以忽略掉device的细节,采取同样的输入输出方式。

对于任何原生的cpp类型,都可以用stream来处理。用户自定义的类,也可以通过重载<<和>>而让stream可以处理。

 1
 2    #include <iostream>
 3    #include <ctime>
 4    #include <sstream>
 5    #include <fstream>
 6     
 7    using namespace std;
 8     
 9    // timestamp returns the current time as a string 
10    std::string timestamp();    
11     
12    class LogStatement;
13    ostream& operator<<(ostream& ost, const LogStatement& ls);
14     
15    class LogStatement
16    {
17    public:
18            LogStatement(std::string s): data(s), time_string( timestamp() )
19            { };
20     
21            //This method handles all the outputs.    
22            friend ostream& operator<<(ostream&, const LogStatement&);   
23    private:
24            std::string data;
25            std::string time_string;
26        
27    };
28     
29    ostream& operator<<(ostream& ost, const LogStatement& ls)
30    {
31            ost<<"~|"<<ls.time_string<<'|'<<ls.data<<"|~";
32            return ost;
33    }
34     
35    std::string timestamp() 
36    {
37            //Notice the use of a stringstream, yet another useful stream medium!
38            ostringstream stream;    
39            time_t rawtime;
40            tm * timeinfo;
41     
42            time(&rawtime);
43            timeinfo = localtime( &rawtime );
44     
45            stream << (timeinfo->tm_year)+1900<<" "<<timeinfo->tm_mon
46            <<" "<<timeinfo->tm_mday<<" "<<timeinfo->tm_hour
47            <<" "<<timeinfo->tm_min<<" "<<timeinfo->tm_sec;
48            // The str() function of output stringstreams return a std::string.
49            return stream.str();   
50    }
51     
52    int main(int argc, char** argv)
53    {
54            if(argc<2)
55            {
56                  // A return of -1 denotes an error condition.
57                  return -1;  
58            }
59            ostringstream log_data;
60            // This takes all the char arrays in the argv 
61            // (except the filename) and produces a stream.
62            for(int i=1;i<argc;i++)    
63            {
64                   log_data<<argv[i]<<' '; 
65            }
66      
67            LogStatement log_entry(log_data.str());
68     
69            clog<<log_entry<<endl;
70     
71            ofstream logfile("logfile",ios::app);
72     
73            // check for errors opening the file
74            if ( ! logfile )      
75            {
76                    return -1;
77            }       
78     
79            logfile<<log_entry<<endl;
80            logfile.close();
81     
82            return 0;
83    }
84

stream大致分为inputstream和outputstream两种,分别对应的类型为std::istream和std::ostream.

stream大概有如下操作:

  *     使用适当的值类型(如stringstream的STD : : string和fstream的文件名)和适当的模式(如用于输入的IOs : : in和用于输出的IOs : : out等,具体取决于流的类型)初始化流
  * 可以通过get和put指针指定I / O应该发生的位置。根据您打开流的方式,可能已经适当地设置了位置(例如,如果使用IOs : : app打开文件,则在流的末尾设置get指针,允许附加)。


seekg(0); seekg(0,ios::beg);         //sets the get pointer to the beginning.
seekg(5,ios::beg);      //sets the get pointer to 5 chars forward of the beginning.
tellp(); tellg()              //returns the current value of the put/get pointer
seekp(-10,ios::end);   //sets the put pointer to 10 chars before the end
seekp(1,ios::cur);      //proceeds to next char

注意:如果需要在一个stream的中间位置插入数据的话,需要手动将指针位置后面的数据移动,否则会被覆盖掉。 * 使用<<或者>>来读或者写。

stream的错误处理

将stream当成bool来处理是比较常见的,

1    ifstream file( "test.txt" );
2    if ( ! file )
3    {
4            cout << "An error occurred opening the file" << endl;
5    }

但是实际上有四种status:

  * good() returns true when everything is okay.
  * bad() returns true when a fatal error has occurred.
  * fail() returns true after an unsuccessful stream operation like an unexpected type of input being encountered.
  * eof() returns true when the end of file is reached.

String Streams

emm,其实string和stream好像挺像的。 区别是,string是可以随机访问的,stream是顺序访问。

 1    #include <iostream>
 2    #include <sstream>
 3     
 4    using namespace std;
 5     
 6    int main()
 7    {
 8            stringstream my_stream(ios::in|ios::out);
 9            std::string dat("Hey, I have a double : 74.79 .");
10     
11            my_stream.str(dat);
12            my_stream.seekg(-7,ios::end);
13     
14            double val;
15            my_stream>>val;
16     
17            val= val*val;
18     
19            my_stream.seekp(-7,ios::end);
20            my_stream<<val;
21     
22            std::string new_val = my_stream.str();
23            cout<<new_val;
24     
25            return 0;
26    }
27

输出是:

Hey, I have a double : 5593.54

我们观察到原本句子末尾的英文句号"."被覆盖掉了。

buffer的使用

I/O操作是相对来说比较花时间的操作,如果我们要多次写很多小文件,那会浪费大量的时间。于是我们的想法是,使用一个临时的Buffer将数据存起来,当这个buffer满了之后再去读或者写。

注意不是所有的stream都采用了这种机制,比如cerr就没有采用。

下面放一段代码来感受下buffer

 1    #include <iostream>
 2    #include <string>
 3    
 4    using std::cout;    using std::endl;
 5    using std::cerr;    using std::ostream;
 6    using std::string;
 7    
 8    /*
 9     * A helpful function that halts the program until
10     * the user presses enter.
11     */
12    void waitForEnter(const string& msg) {
13        cout << msg;
14        string l;
15        std::getline(std::cin, l);
16        cout << endl;
17    }
18    
19    
20    /*
21     * Tests the buffer without flushing anything.
22     *
23     * Tries outputting a string to the stream, then does some slow
24     * operation and then ouputs another string to the stream. We
25     * see nothing is printed for a while then everything is printed at
26     * once, suggesting cout is buffered.
27     */
28    void testBuffer(ostream& os) {
29        os << "Before loop - ";
30        for(int i = 0; i < 2000000000; ++i) {
31            // waste time
32        }
33        os << "After loop" << endl;
34    }
35    
36    /*
37     * Tests the buffer with flushing.
38     *
39     * Here we see that the first string is printed
40     * to the console, and then there is a delay due to
41     * the for loop before the second string is printed.
42     */
43    void testBufferFlush(ostream& os) {
44        os << "Before loop - " << std::flush;
45        for(int i = 0; i < 2000000000; ++i) {
46            // waste time
47        }
48        os << "After loop" << endl;
49    }
50    
51    
52    
53    int main() {
54           waitForEnter("Enter to continue: ");
55           cout << "Testing cout..." << endl;
56           testBuffer(cout);
57           cout << endl;
58    
59           /* Prints a line of 20 = characters. */
60           cout << string(20, '=') << endl;
61    
62           waitForEnter("Enter to continue: ");
63           cout << "Testing flushed cout..." << endl;
64           testBufferFlush(cout);
65           cout << endl;
66    }
67    
68

此处flush的含义是将buffer中的内容立即输出

我们观察发现,在testBUffer中,"before loop"是在循环之后才输出的。暗示cout使用了buffer.