对于C++中的IO操作,博主nbkyzj的三篇文章概括的特别详细,非常值得参考。这三篇文章是:
C++之标准设备IO操作流
C++之预定义类型IO格式控制
C++之文件IO操作流
下文将会部分参考自这三篇文章。
1. C++之标准设备IO操作流
每种语言系统都会提供IO操作的类库,用于对预定义类型数据进行输入输出的操作。C++也是如此,它是以字节流的形式实现的。在输入操作时,字节流从输入设备(键盘、磁盘)流向内存;在输出操作时,字节流由内存流向输出设备(显示器、打印机);字节流可以是ASCII字符、二进制数据、图形图像、音频视频等信息。文件和字符串也可以看成是有序的字节流,又称为文件流和字符串流。C++的编译系统自带一个面向对象的输入输出软件包,即IO流类库。库中各种类的声明都被包含在相应的头文件中,比如之前经常用到的头文件iostream,其中使用的cincout都是常用的流对象,用于对标准设备的IO操作。还有用于用户文件管理IO操作的fstream头文件(磁盘文件的操作),用于字符串流的IO操作的strstream头文件(内存字符流的操作),以及iomanip头文件用于输入输出的格式控制。
在IO流类库中包含以下几个常用的流类:
ios是抽象基类,类istream和ostream是单继承于ios,而类iostream是通过多继承于类istream和类ostream。ios除了派生出istream和ostream还派生出fstreambase文件流类和strstreambase串流类,而该4类又派生出ifstream、ofstream、istrstream、ostrstream,以及fstream和strstream。
在C++中除了上述提到过的预定义流对象cout和cin外,还有非缓冲标准出错流对象cerr和缓冲标准出错流对象clog,它们都是来自于ostream类的对象,用于输出错信息。cerr和clog之间的不同之处在于cerr是不经过缓冲区直接向显示器输出有关信息,而clog则是先把信息放在缓冲区,缓冲区满后或遇上endl时向显示器输出。
2. C++之文件IO操作流
文件是指存放在外部介质上的数据的集合。大家都知道操作系统是以文件为单位来对数据进行管理的。因此如果你要查找外部介质的数据,则先要按文件名找到指定文件,然后再从文件中读取数据,如果要把数据存入外部介质中,如果没有该文件,则先要建立文件,再向它输入数据。由于文件的内容千变万化,大小各不相同,为了统一处理,在C++中用文件流的形式来处理,文件流是以外存文件为输入输出对象的数据流。输出文件流表示从内存流向外存文件的数据,输入文件流则相反。根据文件中数据的组织形式,文件可分为两类:文本文件和二进制文件。文本文件又称为ASCII文件,它的每个字节存放一个ASCII码,代表一个字符。二进制文件则是把内存中的数据,按照其在内存中的存储形式原样写在磁盘上存放。比如一个整数20000,在内存中占两个字节,而按文本形式输出则占5个字节。因此在以文本形式输出时,一个字节对应一个字符,因而便于字符的输出,缺点则是占用存储空间较多。用二进制形式输出数据,节省了转化时间和存储空间,但不能直接以字符的形式输出。
2.1 文件打开
在C++中对文件进行操作分为以下几个步骤:
1)建立文件流对象;
2)打开或建立文件;
3)进行读写操作;
4)关闭文件。
用于文件IO操作的流类主要有三个fstream(输入输出文件流),ifstream(输入文件流)和ofstream(输出文件流);而这三个类都包含在头文件fstream中,所以程序中对文件进行操作必须包含该头文件。
首先建立流对象,然后使用文件流类的成员函数open打开文件,即把文件流对象和指定的磁盘文件建立关联。成员函数open的一般形式为:
文件流对象.open(文件名,使用方式)
其中文件名可以包括路径(如:e:c++file.txt),如果缺少路径,则默认为当前目录。使用方式则是指文件将被如何打开。以下就是文件的部分使用方式,都是ios基类中的枚举类型的值:
此外打开方式有几个注意点:
1)因为nocreate和noreplace,与系统平台相关密切,所以在C++标准中去掉了对它的支持。
2)每一个打开的文件都有一个文件指针,指针的开始位置由打开方式指定,每次读写都从文件指针的当前位置开始。每读一个字节,指针就后移一个字节。当文件指针移到最后,会遇到文件结束符EOF,此时流对象的成员函数eof的值为非0值,表示文件结束。
3)用in方式打开文件只能用于输入数据,而且该文件必须已经存在。
4)用app方式打开文件,此时文件必须存在,打开时文件指针处于末尾,且该方式只能用于输出。
5)用ate方式打开一个已存在的文件,文件指针自动移到文件末尾,数据可以写入到其中。
如果文件需要用两种或多种方式打开,则用"|"来分隔组合在一起。
除了用open成员函数打开文件,还可以用文件流类的构造函数来打开文件,其参数和默认值与open函数完全相同。比如:文件流类 stream(文件名,使用方法);如果文件打开操作失败,open函数的返回值为0,用构造函数打开的话,流对象的值为0。所以无论用哪一种方式打开文件,都需要在程序中测试文件是否成功打开。
在每次对文件IO操作结束后,都需要把文件关闭。那么就需要用到文件流类的成员函数close,一般调用形式:流对象.close()。关闭实际上就是文件流对象和磁盘文件失去关联。
2.2 文件读写
流类库中的IO操作<<、>>、put、get、getline、read和write都可以用于文件的输入输出。
文件写:
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 5 int main() 6 { 7 //打开文件 8 ofstream file("file.txt", ios::out | ios::ate); 9 if (!file) 10 { 11 cout << "不可以打开文件" << endl; 12 exit(1); 13 } 14 15 //写文件 16 file << "hello c++! "; 17 18 char ch; 19 while (cin.get(ch)) 20 { 21 if (ch == ' ') 22 break; 23 file.put(ch); 24 } 25 26 //关闭文件 27 file.close(); 28 29 return 0; 30 }
文件读:
1 #include <iostream> 2 #include <fstream> 3 using namespace std; 4 5 int main() 6 { 7 //打开文件 8 ifstream rfile("file.txt", ios::in); 9 if (!rfile) 10 { 11 cout << "不可以打开文件" << endl; 12 exit(1); 13 } 14 15 //读文件 16 char str[100]; 17 rfile.getline(str, 100);//读到' '终止 18 cout << str << endl; 19 20 char rch; 21 while (rfile.get(rch))//文件指针指向字符‘ ’的下一个 22 { 23 cout.put(rch); 24 } 25 26 cout << endl; 27 28 //关闭文件 29 rfile.close(); 30 31 return 0; 32 }
3. 字符串与整数的相互转换
这一部分主要参考自一博文。
3.1 整数转换为字符串
1)利用itoa
1 #include <iostream> 2 using namespace std; 3 4 int main() 5 { 6 // char *itoa( int value, char *string,int radix); 7 // 原型说明: 8 // value:欲转换的数据。 9 // string:目标字符串的地址。 10 // radix:转换后的进制数,可以是10进制、16进制等。 11 // 返回指向string这个字符串的指针 12 13 int i = 30; 14 char c[8]; 15 itoa(i, c, 16); 16 cout << c << endl; // 1e 17 18 return 0; 19 }
注意:itoa并不是一个标准的C函数,也不是C++一部分。只有部分编译器(如VS)支持。如果要想跨平台,要得sprintf。
2)利用sprintf
1 #include <iostream> 2 #include <stdio.h> 3 using namespace std; 4 5 int main() 6 { 7 // int sprintf( char *buffer, const char *format, [ argument] … ); 8 // 参数列表 9 // buffer:char型指针,指向将要写入的字符串的缓冲区。 10 // format:格式化字符串。 11 // [argument]...:可选参数,可以是任何类型的数据。 12 // 返回值:字符串长度(strlen) 13 14 int i = 30; 15 char c[8]; 16 int length = sprintf(c, "%05X", i); 17 cout << c << endl; // 0001E 18 19 return 0; 20 }
3)利用stringstream
1 #include <iostream> 2 #include <sstream> 3 using namespace std; 4 5 int main() 6 { 7 int i = 30; 8 stringstream ss; 9 ss << i; 10 string s1 = ss.str(); 11 cout << s1 << endl; // 30 12 13 string s2; 14 ss >> s2; 15 cout << s2 << endl; // 30 16 17 return 0; 18 }
stringstream可以吞下不同的类型,根据s2的类型,然后吐出不同的类型。
4)利用使用boost库中的lexical_cast
1 #include <iostream> 2 #include <boost/lexical_cast.hpp> 3 using namespace std; 4 5 int main() 6 { 7 int i = 30; 8 string s = boost::lexical_cast<string>(i); 9 cout << s << endl; // 30 10 11 return 0; 12 }
3.2 字符串转为整数
1)利用strtol(string to long)
1 /* strtol example */ 2 #include <stdio.h> /* printf */ 3 #include <stdlib.h> /* strtol */ 4 5 int main() 6 { 7 // long int strtol(const char* str, char** endptr, int base); 8 9 char szNumbers[] = "2001 60c0c0 -1101110100110100100000 0x6fffff"; 10 char * pEnd; 11 long int li1, li2, li3, li4; 12 li1 = strtol(szNumbers, &pEnd, 10); 13 li2 = strtol(pEnd, &pEnd, 16); 14 li3 = strtol(pEnd, &pEnd, 2); 15 li4 = strtol(pEnd, NULL, 0); 16 printf("The decimal equivalents are: %ld, %ld, %ld and %ld. ", li1, li2, li3, li4); 17 18 return 0; 19 }
2)利用stringstream
1 #include <iostream> 2 #include <sstream> 3 using namespace std; 4 5 int main() 6 { 7 string s = "17"; 8 9 stringstream ss; 10 ss << s; 11 12 int i; 13 ss >> i; 14 cout << i << endl; // 17 15 16 return 0; 17 }
stringstream可以吞下任何类型,根据实际需要吐出不同的类型。
3)利用boost库中的lexical_cast
1 #include <iostream> 2 #include <boost/lexical_cast.hpp> 3 using namespace std; 4 5 int main() 6 { 7 string s = "17"; 8 int i = boost::lexical_cast<int>(s); 9 cout << i << endl; // 17 10 11 return 0; 12 }
4. 数据文件读写
比较常见的是对整数数据的读写,程序如下:
1 #include <iostream> 2 #include <fstream> 3 #include <string> 4 #include <sstream> 5 #include <math.h> 6 using namespace std; 7 8 int main() 9 { 10 // *************************** Write File *************************** // 11 // Open file 12 ofstream wfile("file.txt", ios::out); 13 if (!wfile) 14 { 15 cout << "The file can not be opened!" << endl; 16 exit(1); 17 } 18 19 for (int i = 0; i < pow(2, 16); i++) 20 { 21 stringstream ss; 22 ss << i; 23 string str = ss.str(); 24 wfile << str; 25 wfile << endl; 26 } 27 28 // Close the file 29 wfile.close(); 30 31 // *************************** Read File *************************** // 32 // Open file 33 ifstream rfile("file.txt", ios::in); 34 if (!rfile) 35 { 36 cout << "The file can not be opened!" << endl; 37 exit(1); 38 } 39 40 string line; 41 int num; 42 while (getline(rfile, line)) 43 { 44 // cout << line << endl; 45 stringstream ss; 46 ss << line; 47 ss >> num; 48 cout << num << endl; 49 } 50 51 // Close the file 52 rfile.close(); 53 54 return 0; 55 }