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.