13.IO流与文件
目录介绍
- 13.1 输入输出介绍
- 13.1.1 输入输出流
- 13.1.2 I/O库头文件
- 13.1.3 标准输出流cout
- 13.1.4 标准输入流cin
- 13.1.5 标准错误流cerr
- 13.1.6 标准日志流clog
- 13.2 文件操作实践
- 13.2.1 文件是什么
- 13.2.2 文件类型分类
- 13.2.3 操作文件类
- 13.2.4 文本文件写
- 13.2.5 文本文件读
- 13.2.6 二进制文件写
- 13.2.7 二进制文件读
- 13.2.8 文件读写指针
- 13.3 IO综合案例
13.1 输入输出介绍
13.1.1 输入输出流
学过 C 语言的读者应该知道,它有一整套完成数据读写(I/O)的解决方案:
- 使用 scanf()、gets() 等函数从键盘读取数据,使用 printf()、puts() 等函数向屏幕上输出数据;
- 使用 fscanf()、fgets() 等函数读取文件中的数据,使用 fprintf()、fputs() 等函数向文件中写入数据。
C 语言的这套 I/O 解决方案也适用于 C++ 程序,但 C++ 并没有“偷懒”,它自己独立开发了一套全新的 I/O 解决方案,其中就包含大家一直使用的 cin 和 cout。
- 用 cin 接收从键盘输入的数据,用 cout 向屏幕上输出数据(这 2 个过程又统称为“标准 I/O”)。
- 除此之外,C++ 也对从文件中读取数据和向文件中写入数据做了支持(统称为“文件 I/O”)。
13.1.2 I/O库头文件
ios 是所有流类的基类,它派生出 istream 和 ostream。
特别需要指出的是,为了避免多继承的二义性,从 ios 派生出 istream 和 ostream 时,均使用了 virtual 关键字(虚继承)。
这些流类各自的功能分别为:
- istream:常用于接收从键盘输入的数据;
- ostream:常用于将数据输出到屏幕上;
- ifstream:用于读取文件中的数据;
- ofstream:用于向文件中写入数据;
- iostream:继承自 istream 和 ostream 类,因为该类的功能兼两者于一身,既能用于输入,也能用于输出;
- fstream:兼 ifstream 和 ofstream 类功能于一身,既能读取文件中的数据,又能向文件中写入数据。
13.1.3 标准输出流cout
在C++中,cout是标准输出流对象,用于向控制台或终端输出数据。它是iostream库中的一部分,可以通过包含头文件<iostream>
来使用。
使用cout进行输出非常简单,只需使用<<
运算符将要输出的数据插入到cout对象中即可。以下是一些示例用法:
#include <iostream>
int main() {
int number = 42;
double pi = 3.14159;
std::string message = "Hello, world!";
std::cout << "This is a number: " << number << std::endl;
std::cout << "This is pi: " << pi << std::endl;
std::cout << "This is a message: " << message << std::endl;
return 0;
}
cout对象可以与各种数据类型一起使用,包括基本数据类型(如整数、浮点数、字符等)和自定义类型(如字符串、数组、对象等)。
需要注意的是,cout是一个全局对象,属于std命名空间。因此,在使用cout之前,我们需要使用std::前缀来指定命名空间,或者使用using namespace std;语句来引入整个std命名空间。
13.1.4 标准输入流cin
在C++中,cin是标准输入流对象,用于从控制台或终端读取用户输入的数据。它也是iostream库的一部分,可以通过包含头文件<iostream>
来使用。
使用cin进行输入非常简单,只需使用>>
运算符将输入的数据存储到相应的变量中。以下是一些示例用法:
#include <iostream>
int main() {
int number;
double pi;
std::string message;
std::cout << "Enter a number: ";
std::cin >> number;
std::cout << "Enter the value of pi: ";
std::cin >> pi;
std::cout << "Enter a message: ";
std::cin.ignore(); // 忽略之前的换行符
std::getline(std::cin, message);
std::cout << "You entered: " << number << ", " << pi << ", " << message << std::endl;
return 0;
}
使用cin对象从用户那里读取了一个整数、一个浮点数和一行字符串。每个输入都使用>>运算符将输入的数据存储到相应的变量中。std::getline函数用于读取一行字符串,而std::cin.ignore()函数用于忽略之前的换行符。
13.1.5 标准错误流cerr
cerr是标准错误流对象,用于向控制台或终端输出错误信息。它也是iostream库的一部分,可以通过包含头文件<iostream>
来使用。
使用cerr进行输出也非常简单,只需使用<<
运算符将要输出的错误信息插入到cerr对象中即可。以下是一个示例用法:
#include <iostream>
int main() {
int dividend = 10;
int divisor = 0;
if (divisor == 0) {
std::cerr << "Error: Division by zero!" << std::endl;
} else {
int result = dividend / divisor;
std::cout << "Result: " << result << std::endl;
}
return 0;
}
进行了一个除法运算,但是当除数为零时,会输出一个错误信息到cerr对象中。这样可以将错误信息与正常输出分开,使其更易于识别和处理。
与cout不同的是,cerr对象的输出通常会直接显示在控制台或终端的错误输出流中,而不会被重定向或缓冲。这使得cerr适用于输出紧急或重要的错误信息,以便及时通知用户。
13.1.6 标准日志流clog
clog是标准日志流对象,用于向控制台或终端输出日志信息。它也是iostream库的一部分,可以通过包含头文件<iostream>
来使用。
#include <iostream>
int main() {
int value = 42;
std::clog << "This is a log message: " << value << std::endl;
return 0;
}
使用clog对象输出了一个日志信息,其中包含一个整数值。与cout和cerr不同,clog对象的输出通常会被缓冲,这意味着日志信息可能不会立即显示在控制台或终端上,而是在适当的时机进行刷新。
clog对象通常用于输出一般的日志信息,既不像cout那样用于正常输出,也不像cerr那样用于错误信息。它可以用于记录程序的运行状态、调试信息或其他重要的日志记录。
13.2 文件操作实践
13.2.1 文件是什么
内存中存放的数据在计算机关机后就会消失。要长久保存数据,就要使用硬盘、光盘、U 盘等设备。为了便于数据的管理和检索,引入了“文件”的概念。
成千上万个文件如果不加分类放在一起,用户使用起来显然非常不便,因此又引入了树形目录(目录也叫文件夹)的机制,可以把文件放在不同的文件夹中,文件夹中还可以嵌套文件夹,这就便于用户对文件进行管理和使用。
所谓“格式”,就是关于文件中每一部分的内容代表什么含义的一种约定。 例如,常见的纯文本文件(也叫文本文件,扩展名通常是“.txt”),指的是能够在 Windows 的“记事本”程序中打开,并且能看出是一段有意义的文字的文件。文本文件的格式可以用一句话来描述:文件中的每个字节都是一个可见字符的 ASCII 码。
所谓“文本文件”和“二进制文件”,只是约定俗成的、从计算机用户角度出发进行的分类,并不是计算机科学的分类。因为从计算机科学的角度来看,所有的文件都是由二进制位组成的,都是二进制文件。文本文件和其他二进制文件只是格式不同而已。
13.2.2 文件类型分类
程序运行时产生的数据都属于临时数据,程序一旦运行结束都会被释放
通过文件可以将数据持久化
C++中对文件操作需要包含头文件 ==< fstream >==
文件类型分为两种:
- 文本文件 —— 文件以文本的ASCII码形式存储在计算机中
- 二进制文件 —— 文件以文本的二进制形式存储在计算机中,用户一般不能直接读懂它们
13.2.3 操作文件类
C++ 标准库中还专门提供了 3 个类用于实现文件操作,它们统称为文件流类,这 3 个类分别为:
- ifstream:专用于从文件中读取数据;
- ofstream:专用于向文件中写入数据;
- fstream:既可用于从文件中读取数据,又可用于向文件中写入数据。
13.2.4 文本文件写
写文件步骤如下:
- 包含头文件 :#include <fstream>
- 创建流对象 :ofstream ofs;
- 打开文件 :ofs.open("文件路径",打开方式);
- 写数据 :ofs << "写入的数据";
- 关闭文件 :ofs.close();
文件打开方式:
打开方式 | 解释 |
---|---|
ios::in | 为读文件而打开文件 |
ios::out | 为写文件而打开文件 |
ios::ate | 初始位置:文件尾 |
ios::app | 追加方式写文件 |
ios::trunc | 如果文件存在先删除,再创建 |
ios::binary | 二进制方式 |
注意: 文件打开方式可以配合使用,利用|操作符
**例如:**用二进制方式写文件 ios::binary | ios:: out
示例:
#include <fstream>
void test1() {
ofstream ofs;
ofs.open("2_4.txt" ,ios::out);
ofs << "姓名:张三" << endl;
ofs << "性别:男" << endl;
ofs << "年龄:18" << endl;
ofs.close();
}
int main() {
test1();
return 0;
}
总结:
- 文件操作必须包含头文件 fstream
- 读文件可以利用 ofstream ,或者fstream类
- 打开文件时候需要指定操作文件的路径,以及打开方式
- 利用<<可以向文件中写数据
- 操作完毕,要关闭文件
13.2.5 文本文件读
读文件与写文件步骤相似,但是读取方式相对于比较多。读文件步骤如下:
- 包含头文件 :#include <fstream>
- 创建流对象 :ifstream ifs;
- 打开文件并判断文件是否打开成功 :ifs.open("文件路径",打开方式);
- 读数据 :四种方式读取
- 关闭文件 :ifs.close();
示例:
#include "fstream"
#include "string"
#include "iostream"
using namespace std;
void test() {
ifstream ifs;
ifs.open("text.txt", ios::in);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
//第一种方式
char buf[1024] = {0};
while (ifs >> buf) {
cout << buf << endl;
}
//第二种
// char buf[1024] = {0};
// while (ifs.getline(buf, sizeof(buf))) {
// cout << buf << endl;
// }
//第三种
// string buf;
// while (getline(ifs, buf)) {
// cout << buf << endl;
// }
// char c;
// while ((c = ifs.get()) != EOF) {
// cout << c;
// }
ifs.close();
}
int main() {
test();
return 0;
}
总结:
- 读文件可以利用 ifstream ,或者fstream类
- 利用is_open函数可以判断文件是否打开成功
- close 关闭文件
13.2.6 二进制文件写
以二进制的方式对文件进行读写操作。打开方式要指定为 ==ios::binary==
二进制方式写文件主要利用流对象调用成员函数write
函数原型 :ostream& write(const char * buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
示例:
#include "fstream"
#include "string"
using namespace std;
class Person {
public:
char name[64];
int age;
};
void test() {
//1、包含头文件
//2、创建输出流对象
ofstream ofs("person.txt", ios::out | ios::binary);
//3、打开文件
//ofs.open("person.txt", ios::out | ios::binary);
Person p = {"张三" , 18};
//4、写文件
ofs.write((const char *)&p, sizeof(p));
//5、关闭文件
ofs.close();
}
int main() {
test();
return 0;
}
总结:
- 文件输出流对象 可以通过write函数,以二进制方式写数据
13.2.7 二进制文件读
二进制方式读文件主要利用流对象调用成员函数read
函数原型:istream& read(char *buffer,int len);
参数解释:字符指针buffer指向内存中一段存储空间。len是读写的字节数
示例:
#include "fstream"
#include "string"
#include "iostream"
using namespace std;
class Person {
public:
char name[64];
int age;
};
void test() {
//2、创建输入流对象
ifstream ifs("person.txt", ios::in | ios::binary);
if (!ifs.is_open()) {
cout << "文件打开失败" << endl;
return;
}
//3、打开文件
//ofs.open("person.txt", ios::in | ios::binary);
Person p;
//4、读文件
ifs.read((char *)&p, sizeof(p));
//5、关闭文件
ifs.close();
cout << "姓名: " << p.name << " 年龄: " << p.age << endl;
}
int main() {
test();
return 0;
}
- 文件输入流对象 可以通过read函数,以二进制方式读数据
13.2.8 文件读写指针
- 7.2.3.1 文件错误与状态,文件错误和状态可以通过文件流对象的状态标志来表示。std::fstream类中定义了一些成员函数和状态标志,用于检测和处理文件错误和状态。
- 7.2.3.2 文件的追加,可以使用文件流对象的std::ofstream类来实现文件的追加操作。要实现文件的追加,需要在打开文件时指定追加模式。
- 7.2.3.3 文件结尾的判断,可以使用文件流对象的eof()函数来判断文件是否已经到达结尾。
- 7.2.3.4 在指定位置读/写文件,可以使用文件流对象的seekg()和seekp()函数来在指定位置进行文件的读取和写入操作。
7.3 IO综合案例
7.3.1 IO流实践
- 7.3.1.1 合并两个文件信息
- 7.3.1.2 文件复制
- 7.3.1.3 文件加密
- 7.3.1.4 使用销毁的手段保护文件内容
- 7.3.1.5 在文件中查找关键词出现的次数