文章目录
- 1.文件操作
- 1.1.文件流对象的定义与初始化
- 1.2.文件流的打开模式
- 2.内存操作
1.文件操作
1.1.文件流对象的定义与初始化
- 注意:
ifstream
就是basic_ifstream<char>
,ofstream
就是basic_ofstream<char>
,因为char
类型的更加常用,所以C++中直接就使用char
类型实例化了这种模板,并且命名为ifstream
和ofstream
。 - 简单的实例:注意使用文件流对象需要包含头文件
#include <fstream>
。
#include <iostream>
#include <fstream> // 文件流头文件
#include <string>
int main()
{
// 1.输出流,向文件中输出内容
std::ofstream outFile("my_file");
outFile << "hello\n"; // 这里和std::cout一样的,只不过这里是输出到文件而非终端
// 2.输入流,把文件中的内容全部读进来
std::ofstream inFile("my_file");
std::string x;
inFile >> x;
std::cout << x << std::endl;
}
- 解释文件流的状态:从上面的实例代码可以看到,无论是输入流还是输出流,初始化的时候都需要和一个文件名的
string
相绑定,绑定了之后就相当于这个流IO被打开了,注意是流被打开了,而不是文件被打开了。这样就要求一个流IO只能绑定一个文件,不能绑定多个文件,否则就不知道往哪里输入或者输出了。 - 文件流与文件的关联:使用文件流的缺省构造函数可以创建一个文件流对象, 但是此时不关联任何文件,也就是此时这个文件流是没有打开的,那么就不能对其进行输入输出操作。可以使用
open/close
绑定和解绑文件:
// 默认构造函数,不关联任何文件,此时这个流是关闭的
std::ofstream outFile;
// 输出0
std::cout << outFile.is_open() << std::endl;
// 使用open关联文件,同时打开流,可以输出
outFile.open("my_file");
// 输出1
std::cout << outFile.is_open() << std::endl;
// 解绑文件,同时关闭流,关闭输出
outFile.close();
// 输出0
std::cout << outFile.is_open() << std::endl;
- 注意文件流对象的析构关闭:当使用
close
关闭流的时候,会把缓存中的内容写入文件中。但是如果不显示的调用close
,在文件流对象销毁的时候,其内部析构函数也会自动调用close
关闭流,因此无需显示调用close
。如下代码所示,使用{}
域来设置文件流对象的生命周期,从而让析构函数自动调用close
关闭文件流。
{
std::ofstream outFile("my_file");
outFile << "hello\n";
} // 析构函数自动调用close,缓存内容写入文件中
1.2.文件流的打开模式
- 不同文件流有不同的默认打开模式:
ifstream
默认是std::ios_base::in
,ofstream
默认是std::ios_base::out
,fstream
默认是std::ios_base::in | std::ios_base::out
,注意这个是按位或。 - ate与app的异同: 注意
ate
是打开文件的时候位置位于文件末尾,但是后面可以移动文件流指针到文件的其他地方进行写;但是app
只能在文件末尾进行写,而不能移动文件流的指针。 - trunc模式:截断文件,可以理解为打开文件后会传入源文件中的内容,然后重新写。
- binary模式:禁止系统进行特定的转换,比如换行
\n
,写入文件之后会被转换成换行到下一行,这就是系统特定的转化。而如果使用binary
模式,那么写入的是\n
,文件中输出就是\n
,不会有系统特定的转换。(注意:可能是这个意思?也可能不对) - 合法的打开方式组合:
注意:注意上面一个很tricky的地方,就是输出流的out
模式和out|trunc
模式是等价的,也就是如果是out
模式,每次打开也会把文件清空然后重新写。因此即使是使用out|ate
的模式,结果每次也是打开文件之后先把文件清空,然后把指针放到文件末尾(实际由于文件是空的,此时就是文件开头),这样效果和out
是一样的。因此如果想在文件末尾追加内容,那么一定要是用app
的模式。
2.内存操作
- 注意:
istringstream
就是basic_istringstream<char>
,ostringstream
就是basic_ostirngstream<char>
,因为char
类型的更加常用,所以C++中直接就使用char
类型实例化了这种模板,并且命名为istringstream
和ostringstream
。 - 简单的实例:注意使用内存流需要包含头文件
#include <sstream>
。
#include <iostream>
#include <sstream>
int main()
{
// 1.输出流
std::ostringstream obj1;
obj1 << 1234; // 写入的是1234的整数,这里使用操作符,因此会进行格式化变成字符
std::string res = obj1.str(); // 获取obj1对应的内存,是一个字符串
std::cout << res << std::endl;
// 2.输入流
std::istringstream obj2(res); // 初始化的时候要绑定一块内存
int x;
obj2 >> x; // 把内存中的string转成格式化成int
std::cout << x << std::endl; // 输出1234
}
- 内存流打开模式:注意只有
in/out/ate/app
四种模式,没有trunc/binary
模式。istringstream
默认打开方式是in
,ostringstream
默认打开模式是out
。 - 使用str()方法获取底层对应的字符串:注意不能连续使用
str().c_str()
来获取C风格的字符串,以因为str()
方法返回的是一个string
的右值,如果写成下面的样子,执行完c_res
得到的结果是没有问题的,得到了一个指针,但是这个指针是指向右值的,一旦运行到下面的输出语句,右值就被释放了,这个指针就指向一块无效的内存了,这样是非法的。
auto c_res = obj2.str().c_str();
std::cout << c_res << std::endl;
- 基于字符串流的字符串拼接操作:如果使用
string
进行拼接,每次内存不够了都会进行新的内存开辟、拷贝、释放操作,这样性能低。而使用stringstream
,由于它内部有缓冲区,而且缓冲区内存比较大,所以一般不会频繁的进行内存开辟、拷贝的操作,所以性能更高。
// 1.性能低的方法
std::string x;
x += "Hello";
x += " world";
x += " Hello";
x += " world";
std::cout << x << std::endl;
// 2.高性能方法
std::ostringstream obj;
obj << "Hello";
obj << " world";
obj << " Hello";
obj << " world";
// 这里调用str()方法一次性把所有内容从缓冲区拷贝到内存中
std::cout << obj.str() << std::endl;