迫于拙劣的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。