文件比较和文件流
- 一、文本比较工具 diff
- 1.基本用法
- 1.1输出格式
- 2.常用选项
- 二、文件流
- 1.文件的打开模式
- 2.文件流的分类
- ifstream
- ofstream
- fstrem
- 区别
- 3.文件流的函数
- 1. 构造函数
- 2. is_open 用于判断文件是否打开
- 3. open
- 4. getline
- 5. close
- 6. get()
- 7. read
- 8. write
- 9. put
- 10. gcount
- 11. seekg
- 12. peek
- 13. ignore
- 14. 文件流状态检查函数
一、文本比较工具 diff
- diff 是 文件和内容比较工具,主要用于比较文件的差异、跟踪修改,以及生成补丁。这类工具可以比较文本文件、二进制文件、目录结构等,广泛用于开发、配置管理和系统运维中
1.基本用法
- diff file1 file2 比较两个文件的差异
- 输出:显示如何将 文件1 转换为 文件2,以最小的编辑操作实现
1.1输出格式
diff 的输出结果以 行号 和 更改说明 表示
- <:表示 文件1 中的内容。表示第一个文件独有的行
- >:表示 文件2 中的内容。表示第二个文件独有的行
[行号]动作[行号]
< 文件1中的行
---
> 文件2中的行
动作:
a(add):添加操作,将内容从 文件2 添加到 文件1。
d(delete):删除操作,从 文件1 中删除内容。
c(change):修改操作,将 文件1 的内容替换为 文件2 的内容。
2.常用选项
-u:生成统一格式(unified format)的输出,更易读
-r:递归比较目录
-i:忽略大小写差异
-w:忽略空白字符差异
-b:忽略空行差异
-y:以并排显示模式输出,其中| 表示此行有差异
--suppress-common-lines:隐藏相同的行,
可以生成补丁文件用于修改
diff -u file1 file2 > patch.diff
#应用补丁 //file2是新文件,通过patch.diff的修改,给了file1
patch file1.txt < patch.diff
#撤销补丁,使用R关键字
patch -R file1.txt < patch.diff
二、文件流
- C++的文件流
- 头文件<fstream>
- 允许程序通过文件进行输入(读取数据)和输出(写入数据)。它是 I/O 流库的一部分,主要通过 fstream 类和相关子类来实现。
1.文件的打开模式
- 它们之间可以组合使用
打开模式 | 描述 |
---|---|
std::ios::in | 打开文件以进行输入(读取)(默认用于 ifstream)。如果文件不存在,则操作失败。 |
std::ios::out | 打开文件以进行输出(写入)(默认用于 ofstream)。如果文件不存在,则创建新文件;如果存在,则清空内容。 |
std::ios::app | 打开文件以追加内容到文件末尾。写入的数据保留原内容,不会清空文件。 |
std::ios::ate | 打开文件,并将文件指针定位到文件末尾(可同时进行读写操作)。 |
std::ios::trunc | 如果文件已存在,清空其内容(默认用于 ofstream)(仅在与 std::ios::out 结合使用时生效)。 |
std::ios::binary | 以二进制模式打开文件,而非文本模式。数据读写时不会进行格式转换。 |
std::ios::in | std::ios::out | 以读写模式打开文件,允许同时进行读取和写入操作。 |
std::ios::out | std::ios::app | 打开文件以追加模式写入,保留文件原内容,仅在末尾追加。 |
std::ios::in | std::ios::binary | 以二进制模式打开文件并读取数据。 |
2.文件流的分类
ifstream
- 输入文件流,用于从文件中读取数据。
ofstream
- 输出文件流,用于向文件中写入数据。
fstrem
- 文件流,同时支持从文件读取数据和向文件写入数据(ifstream 和 ofstream 的结合)。
区别
- 虽然 std::fstream 是通用的文件流,可以替代 std::ifstream 和 std::ofstream,但后两者的存在有以下好处
- 明确性:更清楚地表达代码的意图。
- 简洁性:减少代码复杂度,减少模式设置的错误。
- 效率性:为单一任务设计,内部更优化。
- 降低误用风险:避免由于未正确指定模式导致的运行时错误。
3.文件流的函数
1. 构造函数
explicit ifstream(const char* filename, ios_base::openmode mode = ios_base::in);
explicit ofstream(const char* filename, ios_base::openmode mode = ios_base::out);
explicit fstream(const char* filename, ios_base::openmode mode = ios_base::in | ios_base::out);
示例
std::ofstream outfile("example.txt", std::ios::out); // ofstream outfile("example.txt");
/*
std::ofstream outfile;
outfile.open("example.txt",std::ios::out);//outfile.open("example.txt");
*/
std::ifstream infile("example.txt", std::ios::in); // ifstream infile("example.txt");
/*
std::ifstream infile;
infile.open("example.txt", std::ios::in); // infile.open("example.txt");
*/
std::fstream file("example.txt", std::ios::in | std::ios::out); // fstream file("example.txt", ios::in | ios::out);
/*
fstream 必须显式指定打开模式,不能省略 ios::in | ios::out。
std::fstream file;
file.open("example.txt", std::ios::in | std::ios::out);
*/
2. is_open 用于判断文件是否打开
bool is_open() const;
返回值
返回 true:文件成功打开且未关闭
返回 false:文件未打开或已关闭
用法
// 场景1:检查文件是否成功打开
if (!infile.is_open()) {
std::cerr << "无法打开文件" << std::endl;
return -1;
}
// 场景2:循环读取多个文件
std::vector<std::string> filenames = {"file1.txt", "file2.txt", "file3.txt"};
for (const auto& filename : filenames) {
std::ifstream file(filename);
if (!file.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
continue; // 跳过这个文件,继续处理下一个
}
// 处理文件...
file.close();
}
3. open
- 类似于标准文件打开
void open(const char* filename, ios_base::openmode mode = ios_base::in | ios_base::out);
// 或
void open(const string& filename, ios_base::openmode mode = ios_base::in | ios_base::out);
打开模式有如上表
示例
fstream file;
file.open("test.txt", ios::out | ios::in);
4. getline
- getline函数用于从输入流中读取一行文本
// 形式1:从输入流读取到string
istream& getline(istream& is, string& str, char delim = '\n');
// 形式2:从输入流读取到字符数组
istream& getline(istream& is, char* str, streamsize n, char delim = '\n');
参数说明
is: 输入流(如cin或文件流)
str: 存储读取内容的字符串或字符数组
delim: 分隔符(默认为换行符'\n')
n: 最多读取的字符数(用于字符数组形式)
示例:
// 1. 从标准输入读取一行
string line;
getline(std::cin, line);
// 2. 使用自定义分隔符
string data;
getline(cin, data, ','); // 以逗号为分隔符
// 3. 从文件读取所有行
ifstream file("test.txt");
string textLine;
while (getline(file, textLine)) {
cout << textLine << endl;
}
// 返回引用允许我们进行链式操作
string line1, line2;
getline(getline(cin, line1), line2); // 连续读取两行
5. close
- 关闭文件流
#include <fstream>
using namespace std;
int main() {
ofstream file("test.txt");
// 文件操作...
file.close(); // 关闭文件
return 0;
}
void processFile() {
fstream file("test.txt"); // 打开文件
// 文件操作...
// 不需要显式调用close()
// 当file离开作用域时会自动关闭
} // 自动调用析构函数,关闭文件
6. get()
// 主要的几种形式:
int get(); // 形式1:读取单个字符
istream& get(char& ch); // 形式2:读取到字符引用
istream& get(char* str, streamsize n); // 形式3:读取到字符数组
istream& get(char* str, streamsize n, char delim); // 形式4:带分隔符读取
示例
// 逐字符读取文件
void readFileChar() {
ifstream file("test.txt");
char ch;
while (file.get(ch)) {
cout << ch;
}
file.close();
}
// 读取到特定字符为止
void readUntilChar(char delim) {
char buffer[1024];
cin.get(buffer, sizeof(buffer), delim);
cout << "Read: " << buffer << endl;
}
// 处理二进制数据
void processBinaryData() {
ifstream file("data.bin", ios::binary);
char byte;
while (file.get(byte)) {
// 处理每个字节
processBytes(byte);
}
}
// 高效的文件读取
void efficientReading() {
ifstream file("largefile.txt");
constexpr size_t BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
while (file) {
file.get(buffer, BUFFER_SIZE);
// 处理buffer中的数据
}
}
- get()函数保留分隔符在流中
- 读取字符数组时要注意留出空间给结束符’\0’
7. read
- read 函数是一个重要的成员函数,用于以 二进制模式从文件中读取固定数量的字节。它非常适合处理 非文本文件(如图片、音频)或需要高效读取大量数据的场景
istream& read(char* buffer, streamsize count);
char* buffer
缓冲区的大小必须至少为 count,否则可能导致溢出。
一个指向目标缓冲区的指针,读取的数据将存储在这个缓冲区中。
streamsize count
表示要从文件中读取的字节数。
类型为 std::streamsize(通常是一个有符号整数类型)。
返回值
返回对输入流对象的引用(istream&),支持链式操作。
如果读取成功,流的状态仍然有效;如果读取失败(如到达文件末尾),流的状态会变为 "失败状态"。
示例
int main() {
ifstream file("test.bin", ios::binary);
char buffer[100];
// 读取100字节
file.read(buffer, 100);
// 检查实际读取的字节数
cout << "读取了 " << file.gcount() << " 字节" << endl;
file.close();
return 0;
}
读取结构体
struct Student {
char name[50];
int age;
double score;
};
void readStudentData() {
ifstream file("students.dat", ios::binary);
Student student;
// 读取整个结构体
file.read(reinterpret_cast<char*>(&student), sizeof(Student));
cout << "姓名: " << student.name << endl;
cout << "年龄: " << student.age << endl;
cout << "分数: " << student.score << endl;
}
// 读取大文件
void readLargeFile(const string& filename) {
ifstream file(filename, ios::binary);
constexpr size_t BUFFER_SIZE = 4096;
char buffer[BUFFER_SIZE];
while (file) {
file.read(buffer, BUFFER_SIZE);
streamsize bytesRead = file.gcount();
if (bytesRead > 0) {
// 处理读取的数据
processData(buffer, bytesRead);
}
}
}
// 读取数组
void readArray() {
ifstream file("numbers.dat", ios::binary);
int numbers[100];
file.read(reinterpret_cast<char*>(numbers),
sizeof(int) * 100);
}
// 读取固定大小的记录
struct Record {
int id;
char data[256];
};
void readRecord(int position) {
ifstream file("records.dat", ios::binary);
Record record;
// 定位到特定记录
file.seekg(position * sizeof(Record));
file.read(reinterpret_cast<char*>(&record),
sizeof(Record));
}
8. write
- write() 函数是一个用于二进制写入的低级函数,它属于 ostream 类(因此也被 ofstream 继承)。
ostream& write(const char* buffer, streamsize count);
参数
buffer:指向要写入数据的字符缓冲区
count:要写入的字节数
返回对流对象的引用,支持链式操作
按照原始二进制格式写入,不进行任何转换
示例
//写入字符串
ofstream file("test.bin", ios::binary);
const char* str = "Hello";
file.write(str, 5); // 写入5个字节
//写入数值
int number = 42;
file.write(reinterpret_cast<const char*>(&number), sizeof(number));
//写入结构体
struct Person {
char name[20];
int age;
};
Person person = {"John", 25};
file.write(reinterpret_cast<const char*>(&person), sizeof(Person));
//写入数组
int arr[] = {1, 2, 3, 4, 5};
file.write(reinterpret_cast<const char*>(arr), sizeof(arr));
//图像文件处理
class ImageProcessor {
struct BMPHeader {
char signature[2];
uint32_t fileSize;
uint32_t reserved;
uint32_t dataOffset;
// ... 其他头部信息
};
public:
static void convertToBW(const std::string& filename) {
std::fstream file(filename, std::ios::binary | std::ios::in | std::ios::out);
BMPHeader header;
file.read(reinterpret_cast<char*>(&header), sizeof(header));
// 定位到图像数据
file.seekg(header.dataOffset);
std::vector<unsigned char> pixels;
pixels.resize((header.fileSize - header.dataOffset));
file.read(reinterpret_cast<char*>(pixels.data()), pixels.size());
// 转换为黑白
for (size_t i = 0; i < pixels.size(); i += 3) {
unsigned char gray = (pixels[i] + pixels[i+1] + pixels[i+2]) / 3;
pixels[i] = pixels[i+1] = pixels[i+2] = gray;
}
// 写回文件
file.seekp(header.dataOffset);
file.write(reinterpret_cast<char*>(pixels.data()), pixels.size());
}
};
9. put
- put() 函数用于写入单个字符,属于 ostream 类
ostream& put(char ch);
参数
ch:要写入的字符
返回对流对象的引用,支持链式操作
示例
//写入单个字符
ofstream file("test.txt");
file.put('A');
//链式写入多个字符
file.put('H').put('i').put('!');
//配合循环使用
const char* str = "Hello";
for(int i = 0; str[i]; i++) {
file.put(str[i]);
}
//写入特殊字符
file.put('\n'); // 换行符
file.put('\t'); // 制表符
10. gcount
- gcount() 函数返回上一次输入操作读取的字符数,属于 istream 类
treamsize gcount() const;
参数
返回值:返回上一次读取操作实际读取的字符数
示例
//基本使用
ifstream file("test.txt");
char buffer[100];
file.read(buffer, 100);
cout << "读取了 " << file.gcount() << " 个字符" << endl;
//配合getline使用
string line;
getline(file, line);
cout << "本行读取了 " << file.gcount() << " 个字符" << endl;
//错误检查
if (file.read(buffer, 100) && file.gcount() > 0) {
cout << "成功读取数据" << endl;
}
//读取整个文件
ifstream file("data.bin", ios::binary);
vector<char> data;
while (file.read(buffer, sizeof(buffer))) {
data.insert(data.end(), buffer, buffer + file.gcount());
}
11. seekg
seekg() 函数用于设置输入流的读取位置,属于 istream 类
istream& seekg(streampos pos); // 绝对定位
istream& seekg(streamoff off, ios_base::seekdir way); // 相对定位
参数
pos:新的绝对位置
off:相对偏移量
way:移动方向(ios::beg开头,ios::cur当前,ios::end末尾)
示例
//移动到文件开头
ifstream file("test.bin", ios::binary);
file.seekg(0, ios::beg);
//移动到文件末尾
file.seekg(0, ios::end);
//获取文件大小
file.seekg(0, ios::end);
streampos fileSize = file.tellg();
file.seekg(0, ios::beg);
//跳过文件头
struct Header {
int version;
int dataSize;
};
file.seekg(sizeof(Header), ios::beg);
//读取文件中间的数据块
file.seekg(1024, ios::beg); // 跳过前1024字节
char buffer[256];
file.read(buffer, 256);
//在文件中来回移动
int pos = file.tellg(); // 保存当前位置
file.seekg(100, ios::cur); // 向前移动100字节
file.seekg(pos); // 返回之前的位置
12. peek
peek() 函数用于查看输入流中的下一个字符,但不从流中提取它
int peek();
返回值
成功:返回下一个要读取的字符
失败:返回 EOF
不移动流位置指针
示例
//基本使用
ifstream file("test.txt");
char next = file.peek();
cout << "下一个字符是: " << next << endl;
//用于判断行尾
while (file.peek() != EOF && file.peek() != '\n') {
char ch;
file.get(ch);
cout << ch;
}
//检查数字开头
if (isdigit(file.peek())) {
int number;
file >> number;
}
//格式化读取示例
class Parser {
public:
static void parseData(istream& input) {
while (input.peek() != EOF) {
// 跳过空白字符
while (isspace(input.peek())) {
input.ignore();
}
if (isdigit(input.peek())) {
int num;
input >> num;
cout << "Found number: " << num << endl;
}
else if (isalpha(input.peek())) {
string word;
input >> word;
cout << "Found word: " << word << endl;
}
}
}
};
13. ignore
- ignore() 函数用于跳过输入流中的字符
istream& ignore(streamsize n = 1, int delim = EOF);
参数
n: 要忽略的最大字符数,默认为1
delim: 分隔符,读到这个字符就停止,默认为EOF
返回对流的引用
示例
//忽略单个字符
ifstream file("test.txt");
file.ignore(); // 跳过一个字符
//忽略整行
file.ignore(numeric_limits<streamsize>::max(), '\n');
//跳过特定字符前的所有内容
file.ignore(numeric_limits<streamsize>::max(), ':');
//清除缓冲区
cin.ignore(numeric_limits<streamsize>::max(), '\n');
//处理CSV文件示例
class CSVParser {
public:
static vector<string> parseLine(istream& input) {
vector<string> fields;
string field;
while (input.peek() != EOF && input.peek() != '\n') {
if (input.peek() == ',') {
input.ignore(); // 跳过逗号
fields.push_back(field);
field.clear();
}
else {
char ch;
input.get(ch);
field += ch;
}
}
if (!field.empty()) {
fields.push_back(field);
}
input.ignore(); // 跳过换行符
return fields;
}
};
//配合peek()实现高级解析
class DataParser {
public:
static void parseStructuredData(istream& input) {
while (input.peek() != EOF) {
// 跳过注释行
if (input.peek() == '#') {
input.ignore(numeric_limits<streamsize>::max(), '\n');
continue;
}
// 处理数据行
string data;
getline(input, data);
processData(data);
}
}
static void skipWhitespace(istream& input) {
while (input.peek() != EOF && isspace(input.peek())) {
input.ignore();
}
}
static string readToken(istream& input) {
skipWhitespace(input);
string token;
while (input.peek() != EOF && !isspace(input.peek())) {
char ch;
input.get(ch);
token += ch;
}
return token;
}
};
14. 文件流状态检查函数
- 返回值都是bool类型
good() 检查流是否处于良好状态(没有错误)。
eof() 检查是否到达文件末尾。
fail() 检查是否发生了文件流错误(如文件打开失败)。
bad() 检查是否发生了严重错误(如硬件故障)。
clear() 清除流的所有错误状态标志。
//基本检查
ifstream file("data.txt");
if (file.good()) {
cout << "文件流状态正常" << endl;
}
//读取整个文件
ifstream file("input.txt");
string content;
while (!file.eof()) {
char ch;
file.get(ch);
if (!file.eof()) { // 重要:避免重复最后一个字符
content += ch;
}
}
//文件打开检查
ifstream file("config.txt");
if (file.fail()) {
cerr << "无法打开配置文件" << endl;
return;
}
//类型转换错误检查
int number;
cin >> number;
if (cin.fail()) {
cerr << "输入的不是有效数字" << endl;
cin.clear(); // 清除错误状态
cin.ignore(numeric_limits<streamsize>::max(), '\n'); // 清除错误输入
}
//硬件错误检查
ofstream file("data.dat", ios::binary);
file.write(data, size);
if (file.bad()) {
cerr << "发生严重的I/O错误" << endl;
return;
}
//基本使用
ifstream file("data.txt");
if (file.fail()) {
file.clear(); // 清除错误状态
}
//恢复流状态
class StreamResetter {
public:
static void resetStream(istream& stream) {
stream.clear(); // 清除所有错误标志
stream.seekg(0, ios::beg); // 重置读取位置
}
};