C++中文件输入输出主要包含文件输入输出流和文件读写模式的问题。
C++中文件的读写模式
C++ primer中介绍了C++文件读写的6中模式,但是这些模式具体的作用说的不是很明了,特别是trunc模式被翻译为“截断模式”实在是没理解是啥意思,这里进行总结。
文件模式 | 作用 | 使用对象 |
---|---|---|
ios::in | 为读文件而打开 | ifstream, fstream |
ios::out | 为写文件而打开文件,会删除源文件内容,相当于trunc | out | ofstream, fstream |
ios::app | 以追加方式写文件 | ofstream, fstream |
ios::trunc | 如果文件存在先删除,再创建 | ofstream, fstream |
ios::ate | 初始位置在文件末尾 | 任意 |
ios::binary | 二进制方式读写文件 | 任意 |
C++中文件输入输出流都有设置默认的模式,ifstream的默认模式是in,ofstream的默认模式是out,fstream的默认模式是in和out。
如果想要ofstream不覆盖原本文件的内容,要么设置ate模式,要么设置in模式(ofstream可以设置in模式,ifstream也可以设置out模式,但具体作用还有待深究,这里先不纠结)。
C++中文件输入输出流
C++中使用流的过程:首先在内存中定义一个流对象,然后将这个流对象与硬盘中的文件关联起来,通过这个流对象操作文件。
三个基本流fstream,ifstream,ofstream。fstream继承自iostream。
流的方法:fstream.open显示地打开文件(创建流对象和文件的关联),fstream.close关闭流对象,fstream.is_open成功打开并且没有关闭返回true否则false。
流的打开方式:
- 构造对象时直接传入string对象或者C风格字符串来代表文件路径;
- 先构造对象,先创建文件流对象,后续调用open函数。
检查流对象是否成功打开是个好习惯,直接用if语句判断流对象即可。
读取文本文件,要想读取文件必须把文件保存到内存,便需要使用string对象来接收文本。
- 读取整个文件,首先使用ate模式将输入位置指示器移动到文件结尾,再使用tellg获取输入指示器的位置,得到需要构建的内存大小。再使用seekg(0)把输入指示器移动到文件开头,以便对文件进行读取。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main(int argc, char *argv[])
{
for(auto p = argv+1; p != argv+argc; ++p)
{
std::ifstream input(*p, std::ios::ate); // 先将输入指示器移动到文件结尾
if(input)
{
auto size = input.tellg(); // 获取输入指示器的位置
input.seekg(0); // 将输入指示器移动到文件开头
std::cout << size << std::endl;
std::string str(size, '\0');
if(input.read(&str[0], size)) // 从string对象中获取string字符串的初地址
std::cout << str << std::endl;
else
std::cout << "read error" << std::endl;
}
}
return 0;
} - 逐行读取文件,使用getline函数即可getline会去掉原始文本中的换行符,因此此处加上了endl。结果如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, char *argv[])
{
for(auto p = argv+1; p != argv+argc; ++p)
{
std::ifstream input(*p);
std::string str;
while(input)
{
getline(input, str);
std::cout << str << std::endl;
}
}
return 0;
}
1 | hello world |
但是为什么会多出来一个空行呢?我的文件里面没有空行,内容如下:
1 | hello world |
原因是上述循环代码写的有问题,改为如下就正常。原本代码是在while里面判断input条件,相当于是判断input.eof(),但是当读取到文本最后一行时input.eof返回true,于是还会继续读取一行,但是会读取失败,于是最后就多了个endl换行了。正确的写法不是判断当前是否是eof,而是当前按能否读取成功。
1 | while(getline(input, str)) |
注意如果使用fstream的>>重载那么>>遇到空格便会停止一次读取,最终不是读取一行而是读取每个单词。
1 | int main(int argc, char *argv[]) |
结果
1 | hello |
读写二进制文件,使用read和write函数
写文件就没有必要去区分逐行写还是整个写了,只要流没有关闭,便能不停的写入。
两种写文件方式,使用二进制写文件或者从文本输出,C++将自动完成编码和解码工作。
这里使用二进制方式写入文件和读取文件,便能直接将float和int数据写入文件。
当然直接打开Test.b这个文本文件会乱码,内容如下:
1 | ^_<85><EB>Q<B8>^^ @123abc |
1 |
|
输出
1 | read back: 3.14 123 abc |
需要使用iostream&的地方可以传入fstream,这样只需要少量修改便能将标准输入输出改成文件输入输出。
文件流的关闭
一般使用完流对象之后需要及时进行流的关闭。文件流对象会自动的创建和销毁,并且在销毁前会调用close函数。如下函数所示,每次循环都会重新构建文件流对象,在离开每次循环作用域时流对象会被自动销毁,并在销毁前调用构造函数,因此此处不需要手动关闭流对象。
1 |
|