跳过正文
  1. Posts/

C++ IO Streams 学习笔记

·1543 字·4 分钟

迫于拙劣的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
 7using namespace std;
 8
 9// timestamp returns the current time as a string
10std::string timestamp();
11
12class LogStatement;
13ostream& operator<<(ostream& ost, const LogStatement& ls);
14
15class LogStatement
16{
17public:
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&);
23private:
24        std::string data;
25        std::string time_string;
26
27};
28
29ostream& operator<<(ostream& ost, const LogStatement& ls)
30{
31        ost<<"~|"<<ls.time_string<<'|'<<ls.data<<"|~";
32        return ost;
33}
34
35std::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
52int 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}

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

stream大概有如下操作:

  • 使用适当的值类型(如 stringstream 的 std::string 和 fstream 的文件名)和适当的模式(如用于输入的 std::ios::in 和用于输出的 std::ios::out 等,具体取决于流的类型)初始化流。
  • 可以通过 get 和 put 指针指定 I/O 应该发生的位置。根据打开流的方式,可能已经适当地设置了位置(例如,如果使用 std::ios::app 打开文件,则在流的末尾设置 get 指针,允许附加)。
1seekg(0); seekg(0,ios::beg);         //sets the get pointer to the beginning.
2seekg(5,ios::beg);      //sets the get pointer to 5 chars forward of the beginning.
3tellp(); tellg()              //returns the current value of the put/get pointer
4seekp(-10,ios::end);   //sets the put pointer to 10 chars before the end
5seekp(1,ios::cur);      //proceeds to next char

注意:如果需要在一个stream的中间位置插入数据的话,需要手动将指针位置后面的数据移动,否则会被覆盖掉。

  • 使用 << 或者 >> 来读或者写。

stream的错误处理
#

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

1ifstream file( "test.txt" );
2if ( ! 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
 4using namespace std;
 5
 6int 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}

输出是:

1Hey, I have a double : 5593.54

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

buffer的使用
#

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

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

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

详细代码
 1#include <iostream>
 2#include <string>
 3
 4using std::cout;    using std::endl;
 5using std::cerr;    using std::ostream;
 6using std::string;
 7
 8/*
 9 * A helpful function that halts the program until
10 * the user presses enter.
11 */
12void 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 */
28void 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 */
43void 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
53int 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}

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

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

相关文章

Eigen: C++开源矩阵学习笔记

·736 字·2 分钟
接触 Eigen 的原因是最近在看 caffe/caffe2 源码,caffe2 中使用了 Eigen 库。Eigen 是一个基于 C++ 模板的线性代数库,直接将库下载后放在项目目录下,然后包含头文件就能使用,非常方便。对于 Linux 用户,只需要把头文件放到 /usr/include 下即可。此外,Eigen 的接口清晰,稳定高效。