C++ 文件IO
Stream In Computer
iostream, fstream, stringstream……所以stream(流)到底是个啥。
在上次的一篇文章中说到,stream的本质就是一个“线性”数据结构:所谓线性(当然不是说满足线性空间的八大条件)是指其有类似于数列的结构,你可以在上面通过读头head
来顺序访问。
还是举个例子比较好,就拿最简单的stringstream举例子:
首先,我们考察这样的字符串
"123 456 1.3"
将其作为一个stream,我们假设此时读头指向第一个字符:
"123 456 1.3"
^(head)
那么,其线性体现在 head 可以在其上进行移动,例如前进4位seek(4)
:
seek(4 /*前进 4 步*/, ios::cur /* 从当前位置开始数 */)
"123 456 1.3"
^(head)
访问是指,可以读取当前读头下的值:
get() // return '4'
"123 456 1.3"
^(head)
可以写入当前读头下的值:
put('7')
"123 476 1.3"
^(head)
通常情况下,get
和put
函数还会使得head
向前前进一步。同时,为了更好的在stream上定位,其还提供获取当前位置(相对于起始位置)的方法:
tell() // return 6
"123 456 1.3"
^(head)
总结一下,stream是这样一个线性数据结构:
seek
:设置head位置tell
:获取head的相对于put
:将值写入head位置,并前进get
:获取head下的值,并前进
被封装的stream
那iostream帮你做了啥呢,简而言之,就是在这样的数据结构上帮你做了常用类型在流上的读写。
比如,我们的输出流cout
cout << 13;
他在控制台输出了13
,但是如何实现的呢?
假设我们之前输出了:
"abca 123 " // this is our `cout` stream.
^(head)
这里head指向了流的末尾
那么后一步:
"abca 123 13" // this is our `cout` stream.
^(head)
13被我们输出到了流的末尾。
那 cin 做了啥呢,假设我们输入了"123 456 1.3"
并执行如下的代码:
cin >> n >> m >> x; // assume n, m is int, x is double
执行过程如下:
// 啥都没读
"123 456 1.3"
^(head)
// read n
"123 456 1.3"
^(head)
// read m
"123 456 1.3"
^(head)
// read x
"123 456 1.3"
^(head)
// done.
文件流
好吧,至此你应该理解文件和你手动输入实际上没啥区别了,都只是在一个流结构上进行读写罢了。使用文件流需要你加一句。
#include <fstream>
常见的文件操作如下:
打开文件
ifstream file("input.txt"); // ifstream stand for input file stream
ofstream file("output.txt"); // ofstream stand for output file stream
我推荐使用这样的语法,符合C++RAII的原则。
这样写等价于课上提到的:
ifstream file;
file.open("input.txt");
判断是否打开
我们处理文件的第一个事情一定是判断是否成功打开了文件。
file.is_open()
非常重要!
读写
读写实际上和cin/cout
是相通的。
只不过读的时候,通常需要判断是否文件已经读完:
file.eof() // eof() stand for End Of File
例如,在cout中输出一个文件的所有内容:
while (! file.eof()) {
char c;
c = file.get();
cout << c;
}
课上类似于(不推荐现在写!)
while (s >> n) {
std::cout << n << '\n';
}
解释需要类的知识:https://en.cppreference.com/w/cpp/io/basic_ios/operator_bool
关闭文件
实际上没必要,因为遵循raii的原则,c++应该在destructor上自动关该文件。
file.close();
说实话c++的文件还挺复杂的,不需要在上面花太多时间。
A demo
请在你的作业中使用绝对路径!
e.g.
D:\\cpp-homework\\data.txt
(for windows)~/Documents/my_homework/data.txt
(for mac)
读入一个文件的所有内容并打印在控制台上
#include <iostream>
#include <fstream>
using namespace std;
int main () {
// Declare a input file stream.
ifstream input_file;
// 1. Open the file: full path is recommended here.
input_file.open("D:\\data\\input_data.txt");
// 2. Check the file is opend correctly:
if (! input_file.is_open()) {
// error: file is not opened.
cout << "Cannot open file." << endl;
// exit the program.
return -1;
}
// 3. Read the file, until End Of File (eof)
while (! input_file.eof()) {
char c;
// Get one char from file.
input_file.get(c);
// If the operation failed, break the loop.
if (! input_file.good()) {
break;
}
// else print the char c in console.
cout << c;
}
// 3. (Optional) Close the file.
input_file.close();
return 0;
}
输出所有素数到文件:
#include <iostream>
#include <fstream>
using namespace std;
bool is_prime(int n) {
if (n == 1) {
return false;
}
for (int i = 2; i < n - 1; ++i) {
if (n % i == 0) {
return false;
}
}
return true;
}
int main () {
// Declare an output file stream.
ofstream output_file;
// 1. Open the file: full path is recommended here.
output_file.open("D:\\Data\\output_data.txt");
// 2. Check the file is opend correctly:
if (! output_file.is_open()) {
// error: file is not opened.
cout << "Cannot open file." << endl;
// exit the program.
return -1;
}
// 3. write the file, the operation is similar to cout.
for (int i = 1; i < 100; ++i) {
// Here, write the primes between [1, 100) to output_file.
if (is_prime(i)) {
output_file << i << endl;
}
}
// 3. (Optional) Close the file.
output_file.close();
return 0;
}