C标准库文件读写
文本文件写入
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
const char* filename = "ctest.txt";
FILE* fp = fopen(filename, "w");
if (fp == nullptr) {
perror("打开文件失败");
return;
}
// 写入字符串
fprintf(fp, "姓名: %s\n", "张三");
fprintf(fp, "年龄: %d\n", 25);
fprintf(fp, "分数: %.2f\n", 89.5);
// 写入单个字符
fputc('\n', fp);
fclose(fp);
写模式参考
| 模式 | 读 | 写 | 文件不存在 | 文件存在时 | 行为 |
|---|---|---|---|---|---|
"w" | ✗ | ✓ | 创建 | 覆盖 | 从头写入 |
"a" | ✗ | ✓ | 创建 | 保留 | 追加到末尾 |
"w+" | ✓ | ✓ | 创建 | 覆盖 | 从头写入(可读) |
"a+" | ✓ | ✓ | 创建 | 保留 | 追加到末尾(读可任意位置) |
"r+" | ✓ | ✓ | 失败 | 保留 | 从当前位置覆盖写入 |
加
b得二进制版本(如"wb","ab","w+b"/"wb+"等),详见二进制章节。Windows 上b抑制\n↔\r\n转换,POSIX 无影响。
C11 独占模式(x): "wx", "w+x"——文件已存在则打开失败,防止意外覆盖,打开后行为同对应的 w/w+。二进制版本为 "wbx", "w+bx"/"wb+x"。
文本文件读取
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
const char* filename = "ctest.txt";
FILE* fp = fopen(filename, "r");
if (fp == nullptr) {
perror("打开文件失败");
return;
}
// 方式1:逐行读取
char line[256];
while (fgets(line, sizeof(line), fp) != nullptr) {
printf("读取: %s", line);
}
// 方式2:格式化读取
rewind(fp); // 重置到文件开头
char name[50];
int age;
float score;
fscanf(fp, "姓名: %s", name);
fscanf(fp, "年龄: %d", &age);
fscanf(fp, "分数: %f", &score);
fclose(fp);
读模式参考
| 模式 | 读 | 写 | 文件不存在 | 说明 |
|---|---|---|---|---|
"r" | ✓ | ✗ | 失败 | 只读,从开头读 |
"r+" | ✓ | ✓ | 失败 | 读写,写入行为见写模式参考 |
加
b得二进制版本("rb","rb+"/"r+b"),详见二进制章节。"a+"、"w+"也支持读取,详见写模式参考。
二进制文件写入
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
const char* filename = "cbinary.dat";
// 定义结构体
struct Student {
int id;
char name[50];
float score;
};
FILE* fp = fopen(filename, "wb");
if (fp == nullptr) {
perror("打开文件失败");
return;
}
Student s1 = {1, "张三", 85.5};
Student s2 = {2, "李四", 90.0};
// 写入结构体
fwrite(&s1, sizeof(Student), 1, fp);
fwrite(&s2, sizeof(Student), 1, fp);
fclose(fp);
二进制写模式
| 模式 | 等效文本模式 | 行为 | 说明 |
|---|---|---|---|
"wb" | "w" | 覆盖 | 创建或清空后写入 |
"ab" | "a" | 追加 | 写入始终追加到末尾 |
"wb+" / "w+b" | "w+" | 覆盖 | 从头写入(可读) |
"ab+" / "a+b" | "a+" | 追加 | 追加到末尾(读可任意位置) |
"rb+" / "r+b" | "r+" | 定位写入 | 文件必须存在,从当前位置写入 |
b抑制 Windows 上的\n↔\r\n转换,POSIX 无影响。二进制写入行为与文本版本相同,仅换行处理不同。
C11 独占:"wbx","w+bx"/"wb+x"——文件已存在则失败。
二进制文件读取
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
const char* filename = "cbinary.dat";
// 定义结构体
struct Student {
int id;
char name[50];
float score;
};
FILE* fp = fopen(filename, "rb");
if (fp == nullptr) {
perror("打开文件失败");
return;
}
// 获取文件大小
fseek(fp, 0, SEEK_END);
long fileSize = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("文件大小: %ld 字节\n", fileSize);
// 读取结构体
Student s;
while (fread(&s, sizeof(Student), 1, fp) == 1) {
printf("ID: %d, 姓名: %s, 分数: %.2f\n", s.id, s.name, s.score);
}
fclose(fp);
二进制读模式
| 模式 | 等效文本模式 | 说明 |
|---|---|---|
"rb" | "r" | 只读,从开头读 |
"rb+" / "r+b" | "r+" | 读写,写入行为见二进制写模式 |
"ab+"、"wb+"等也支持读取,详见二进制写模式。
文件位置操作
#include <cstdio>
#include <cstdlib>
FILE* fp = fopen("test.txt", "w+");
// 写入数据
fputs("Hello World\n", fp);
fputs("Second Line\n", fp);
// 获取当前位置
long pos = ftell(fp);
printf("当前位置: %ld\n", pos);
// 移动到文件开头
fseek(fp, 0, SEEK_SET);
// 移动到文件末尾
fseek(fp, 0, SEEK_END);
// 从当前位置偏移
fseek(fp, -5, SEEK_CUR);
// 重置到开头
rewind(fp);
fclose(fp);
C++标准库文件读写
文本文件写入
基本写入
#include <iostream>
#include <fstream>
#include <string>
std::ofstream outFile(filename);
if (!outFile.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
return;
}
// 写入多行文本
outFile << "第一行内容" << std::endl;
outFile << "第二行内容" << std::endl;
outFile << "数字: " << 42 << std::endl;
outFile << "浮点数: " << 3.14159 << std::endl;
outFile.close();
std::cout << "文件写入成功" << std::endl;
追加模式写入
#include <iostream>
#include <fstream>
#include <string>
// std::ios::app 表示追加模式
std::ofstream outFile(filename, std::ios::app);
if (!outFile.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
return;
}
outFile << "这是追加的内容" << std::endl;
outFile.close();
写模式参考
std::ofstream 默认 out | trunc(等效 C "w"):
| 标志组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
out | "w" | 覆盖 | 创建或清空后写入 |
out | app | "a" | 追加 | 写入始终追加到末尾 |
in | out | "r+" | 定位写入 | 文件必须存在,从当前位置覆盖写入 |
in | out | trunc | "w+" | 覆盖 | 创建或清空后读写 |
in | out | app | "a+" | 追加 | 读可任意位置,写追加到末尾 |
加
\| binary得二进制版本,详见二进制章节。app和ate的区别:app在每次写入前自动定位到末尾;ate只在打开时定位一次,之后可seekp到任意位置。trunc只有指定了out才生效。
文本文件读取
#include <iostream>
#include <fstream>
#include <string>
std::ifstream inFile(filename);
if (!inFile.is_open()) {
std::cerr << "无法打开文件: " << filename << std::endl;
return;
}
// 方式1:逐行读取
std::string line;
std::cout << "=== 逐行读取 ===" << std::endl;
while (std::getline(inFile, line)) {
std::cout << line << std::endl;
}
// 重置文件指针到开头
inFile.clear();
inFile.seekg(0, std::ios::beg);
// 方式2:逐词读取
std::string word;
std::cout << "\n=== 逐词读取 ===" << std::endl;
while (inFile >> word) {
std::cout << word << " ";
}
std::cout << std::endl;
inFile.close();
读模式参考
std::ifstream 默认 in(等效 C "r"):
| 标志组合 | 等效 C 模式 | 说明 |
|---|---|---|
in | "r" | 只读,文件必须存在 |
in | out | "r+" | 读写,写入行为见写模式参考 |
加
\| binary得二进制版本,详见二进制章节。in | out | app("a+")、in | out | trunc("w+")等也支持读取,详见写模式参考。
使用 fstream(读写一体)
#include <fstream>
#include <iostream>
// 以读写模式打开文件
std::fstream file(filename, std::ios::in | std::ios::out | std::ios::app);
if (!file.is_open()) {
std::cerr << "无法打开文件" << std::endl;
return;
}
// 读取内容
std::string line;
while (std::getline(file, line)) {
std::cout << line << std::endl;
}
// 写入新内容
file << "新添加的内容" << std::endl;
file.close();
打开模式标志速查
| 标志 | 含义 |
|---|---|
std::ios::in | 以读取方式打开文件 |
std::ios::out | 以写入方式打开文件 |
std::ios::app | 追加模式,每次写入前定位到文件末尾 |
std::ios::ate | 打开后立即定位到文件末尾(At The End) |
std::ios::trunc | 若文件存在则截断(清空内容) |
std::ios::binary | 二进制模式(Windows 上抑制换行符转换) |
各流类型的默认模式:
| 流类型 | 默认模式 | 等效 C 模式 |
|---|---|---|
std::ifstream | std::ios::in | "r" |
std::ofstream | std::ios::out | std::ios::trunc | "w" |
std::fstream | std::ios::in | std::ios::out | "r+" |
写模式组合和写入行为汇总已归入上方「写模式参考」,读模式已归入「读模式参考」。
二进制文件写入
基本写入
#include <iostream>
#include <fstream>
#include <vector>
// 定义一个结构体用于二进制读写
struct Student {
int id;
char name[50];
double score;
};
std::ofstream outFile(filename, std::ios::binary);
if (!outFile.is_open()) {
std::cerr << "无法打开文件" << std::endl;
return;
}
// 写入单个结构体
Student s1 = {1, "张三", 85.5};
outFile.write(reinterpret_cast<const char*>(&s1), sizeof(Student));
// 写入多个结构体
Student students[] = {
{2, "李四", 90.0},
{3, "王五", 78.5}
};
outFile.write(reinterpret_cast<const char*>(students), sizeof(students));
// 写入原始字节数组
std::vector<char> buffer = {'H', 'e', 'l', 'l', 'o'};
outFile.write(buffer.data(), buffer.size());
outFile.close();
二进制写模式
| 标志组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
out | binary | "wb" | 覆盖 | 创建或清空后写入 |
out | app | binary | "ab" | 追加 | 写入始终追加到末尾 |
in | out | binary | "r+b" | 定位写入 | 文件必须存在,从当前位置写入 |
in | out | trunc | binary | "w+b" | 覆盖 | 创建或清空后读写 |
in | out | app | binary | "a+b" | 追加 | 读可任意位置,写追加到末尾 |
binary抑制 Windows 上的\n↔\r\n转换,POSIX 无影响。行为与对应文本模式相同,仅换行处理不同。
二进制文件读取
#include <iostream>
#include <fstream>
#include <vector>
// 定义一个结构体用于二进制读写
struct Student {
int id;
char name[50];
double score;
};
std::ifstream inFile(filename, std::ios::binary);
if (!inFile.is_open()) {
std::cerr << "无法打开文件" << std::endl;
return;
}
// 获取文件大小
inFile.seekg(0, std::ios::end);
std::streamsize fileSize = inFile.tellg();
inFile.seekg(0, std::ios::beg);
std::cout << "文件大小: " << fileSize << " 字节" << std::endl;
// 读取单个结构体
Student s;
inFile.read(reinterpret_cast<char*>(&s), sizeof(Student));
std::cout << "ID: " << s.id
<< ", 姓名: " << s.name
<< ", 分数: " << s.score << std::endl;
// 读取到缓冲区
std::vector<char> buffer(fileSize);
inFile.seekg(0, std::ios::beg);
inFile.read(buffer.data(), fileSize);
inFile.close();
二进制读模式
| 标志组合 | 等效 C 模式 | 说明 |
|---|---|---|
in | binary | "rb" | 只读,从开头读 |
in | out | binary | "r+b" | 读写,写入行为见二进制写模式 |
in | out | app | binary("a+b")等也支持读取,详见二进制写模式。
流缓冲区写入优化
#include <iostream>
#include <fstream>
#include <vector>
std::ofstream outFile(filename, std::ios::binary);
// 使用缓冲区提高性能
std::vector<char> buffer(1024 * 1024); // 1MB 缓冲区
outFile.rdbuf()->pubsetbuf(buffer.data(), buffer.size());
// 大量数据写入
for (int i = 0; i < 10000; ++i) {
outFile.write(reinterpret_cast<const char*>(&i), sizeof(int));
}
流缓冲区读取优化
#include <iostream>
#include <fstream>
#include <vector>
std::ifstream inFile(filename, std::ios::binary);
std::vector<char> buffer(1024 * 1024);
inFile.rdbuf()->pubsetbuf(buffer.data(), buffer.size());
int value;
while (inFile.read(reinterpret_cast<char*>(&value), sizeof(int))) {
// 处理数据
}
文件位置操作
#include <iostream>
#include <fstream>
std::fstream file(filename, std::ios::in | std::ios::out | std::ios::trunc);
// 写入一些数据
for (int i = 0; i < 10; ++i) {
file << "Line " << i << std::endl;
}
// 获取当前写入位置
std::streampos writePos = file.tellp();
std::cout << "当前写入位置: " << writePos << std::endl;
// 移动到文件开头
file.seekp(0, std::ios::beg);
// 移动到文件末尾
file.seekp(0, std::ios::end);
// 从当前位置向前移动
file.seekp(-10, std::ios::cur);
// 切换到读取模式,获取读取位置
file.seekg(0, std::ios::beg);
std::streampos readPos = file.tellg();
file.close();
Qt 文件读写
使用 QFile
基础文件操作
#include <QFile>
#include <QDebug>
#include <QFileInfo>
QString filename = "test.txt";
// 检查文件是否存在
QFile file(filename);
if (file.exists()) {
qDebug() << "文件存在";
}
// 获取文件信息
QFileInfo fileInfo(filename);
qDebug() << "文件名:" << fileInfo.fileName();
qDebug() << "文件大小:" << fileInfo.size() << "字节";
qDebug() << "创建时间:" << fileInfo.birthTime();
qDebug() << "修改时间:" << fileInfo.lastModified();
文本文件写入(QFile + QTextStream)
#include <QFile>
#include <QDebug>
#include <QTextStream>
QFile file(filename);
// 打开文件,只写模式,文本模式
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qDebug() << "无法打开文件:" << file.errorString();
return;
}
// 使用 QTextStream 写入
QTextStream out(&file);
out.setCodec("UTF-8"); // 设置编码
out << "第一行内容" << Qt::endl;
out << "第二行内容" << Qt::endl;
out << "整数: " << 42 << Qt::endl;
out << "浮点数: " << 3.14159 << Qt::endl;
out << "字符串: " << QString("Hello Qt") << Qt::endl;
file.close();
qDebug() << "文件写入成功";
追加模式写入
#include <QFile>
#include <QDebug>
#include <QTextStream>
QFile file(filename);
// Append 模式
if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Append)) {
qDebug() << "无法打开文件:" << file.errorString();
return;
}
QTextStream out(&file);
out << "这是追加的内容" << Qt::endl;
file.close();
写模式参考
Qt 默认二进制模式,加 | Text 切换文本模式(Windows 换行转换)。以下为文本模式组合:
| Qt 组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
WriteOnly | Text | — | 定位写入 | 从开头写入,不覆盖尾部旧数据(Qt 特有) |
WriteOnly | Truncate | Text | "w" | 覆盖 | 清空后写入,等同 C "w" |
WriteOnly | Append | Text | "a" | 追加 | 写入始终追加到末尾 |
ReadWrite | Text | "r+" | 定位写入 | 文件必须存在,从当前位置写入 |
ReadWrite | Truncate | Text | "w+" | 覆盖 | 清空后读写 |
ReadWrite | Append | Text | "a+" | 追加 | 读可任意位置,写追加到末尾 |
WriteOnly | NewOnly | Text | "wx" | 覆盖 | 仅新建,文件已存在则失败 |
ReadWrite | NewOnly | Text | — | 覆盖 | 仅新建后读写,文件已存在则失败 |
⚠ 关键差异: Qt 的
WriteOnly默认不清空文件(与 C"w"和 C++out不同)。需要等同 C"w"的行为时,必须显式加Truncate。去掉\| Text即得对应二进制模式,详见二进制章节。
文本文件读取(QFile + QTextStream)
#include <QFile>
#include <QDebug>
#include <QTextStream>
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qDebug() << "无法打开文件:" << file.errorString();
return;
}
QTextStream in(&file);
in.setCodec("UTF-8");
// 方式1:逐行读取
qDebug() << "=== 逐行读取 ===";
while (!in.atEnd()) {
QString line = in.readLine();
qDebug() << line;
}
// 重置到文件开头
file.seek(0);
in.reset();
// 方式2:读取所有内容
qDebug() << "\n=== 读取全部 ===";
QString allContent = in.readAll();
qDebug() << allContent;
file.close();
读模式参考
文本模式读取(加 | Text):
| Qt 组合 | 等效 C 模式 | 说明 |
|---|---|---|
ReadOnly | Text | "r" | 只读,文件必须存在 |
ReadWrite | Text | "r+" | 读写,写入行为见写模式参考 |
去掉
\| Text即得二进制版本,详见二进制章节。ReadWrite \| Append \| Text("a+")等也支持读取,详见写模式参考。
打开模式标志速查
| 标志 | 含义 |
|---|---|
QIODevice::ReadOnly | 只读 |
QIODevice::WriteOnly | 只写 |
QIODevice::ReadWrite | 读写(ReadOnly | WriteOnly) |
QIODevice::Append | 追加模式,每次写入前定位到文件末尾 |
QIODevice::Truncate | 打开时截断文件(清空内容) |
QIODevice::Text | 文本模式(Windows 下转换换行符) |
QIODevice::Unbuffered | 绕过缓冲区,直接读写 |
QIODevice::NewOnly | 仅当文件不存在时才创建(类似 C11 x) |
QIODevice::ExistingOnly | 仅当文件已存在时才打开 |
写模式组合和写入行为汇总已归入上方「写模式参考」。
二进制文件写入(QDataStream)
基本写入
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QDateTime>
// 定义数据结构
struct Product {
int id;
QString name;
double price;
QDateTime createTime;
};
QFile file(filename);
if (!file.open(QIODevice::WriteOnly)) {
qDebug() << "无法打开文件:" << file.errorString();
return;
}
QDataStream out(&file);
// 设置版本(保证兼容性)
out.setVersion(QDataStream::Qt_5_15);
// 写入魔数和版本标识
out << quint32(0xA0B0C0D0); // 魔数
out << qint32(1); // 文件版本
// 写入基本数据类型
out << qint32(42);
out << QString("Hello Qt");
out << double(3.14159);
out << true;
// 写入列表
QStringList list;
list << "Item1" << "Item2" << "Item3";
out << list;
// 写入自定义结构
out << qint32(1); // ID
out << QString("Product A");
out << double(99.99);
out << QDateTime::currentDateTime();
file.close();
qDebug() << "二进制文件写入成功";
二进制写模式
Qt 默认即二进制模式(无需 | Text):
| Qt 组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
WriteOnly | — | 定位写入 | 从开头写入,不覆盖尾部旧数据(Qt 特有) |
WriteOnly | Truncate | "wb" | 覆盖 | 清空后写入 |
WriteOnly | Append | "ab" | 追加 | 写入始终追加到末尾 |
ReadWrite | "r+b" | 定位写入 | 文件必须存在,从当前位置写入 |
ReadWrite | Truncate | "w+b" | 覆盖 | 清空后读写 |
ReadWrite | Append | "a+b" | 追加 | 读可任意位置,写追加到末尾 |
WriteOnly | NewOnly | "wbx" | 覆盖 | 仅新建,文件已存在则失败 |
ReadWrite | NewOnly | — | 覆盖 | 仅新建后读写 |
加
\| Text切换文本模式(见文本章节)。
二进制文件读取(QDataStream)
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QDateTime>
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "无法打开文件:" << file.errorString();
return;
}
QDataStream in(&file);
in.setVersion(QDataStream::Qt_5_15);
// 读取魔数和版本
quint32 magic;
qint32 version;
in >> magic >> version;
if (magic != 0xA0B0C0D0) {
qDebug() << "文件格式错误";
file.close();
return;
}
// 读取基本数据类型
qint32 intValue;
QString stringValue;
double doubleValue;
bool boolValue;
in >> intValue >> stringValue >> doubleValue >> boolValue;
qDebug() << "整数:" << intValue;
qDebug() << "字符串:" << stringValue;
qDebug() << "浮点数:" << doubleValue;
qDebug() << "布尔值:" << boolValue;
// 读取列表
QStringList list;
in >> list;
qDebug() << "列表:" << list;
// 读取自定义结构
qint32 id;
QString name;
double price;
QDateTime createTime;
in >> id >> name >> price >> createTime;
qDebug() << "产品ID:" << id;
qDebug() << "产品名:" << name;
qDebug() << "价格:" << price;
qDebug() << "创建时间:" << createTime;
file.close();
二进制读模式
Qt 默认即二进制(无需 | Text):
| Qt 组合 | 等效 C 模式 | 说明 |
|---|---|---|
ReadOnly | "rb" | 只读,文件必须存在 |
ReadWrite | "r+b" | 读写,写入行为见二进制写模式 |
加
\| Text切换文本模式(见文本章节)。ReadWrite \| Append("a+b")等也支持读取。
原始字节写入
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QDateTime>
QFile file(filename);
file.open(QIODevice::WriteOnly);
QByteArray data;
data.append("Hello World");
data.append(0x00);
data.append(0xFF);
file.write(data);
file.close();
原始字节读取
#include <QFile>
#include <QDataStream>
#include <QDebug>
#include <QDateTime>
QFile file(filename);
file.open(QIODevice::ReadOnly);
// 方式1:读取全部
QByteArray allData = file.readAll();
// 方式2:分块读取
file.seek(0);
const int bufferSize = 1024;
while (!file.atEnd()) {
QByteArray chunk = file.read(bufferSize);
// 处理 chunk
}
file.close();
内存映射文件(QFile + mmap)
#include <QFile>
#include <QDebug>
QFile file(filename);
if (!file.open(QIODevice::ReadOnly)) {
qDebug() << "无法打开文件";
return;
}
// 内存映射(只读)
uchar* memory = file.map(0, file.size());
if (memory) {
// 直接访问内存中的文件内容
QByteArray data(reinterpret_cast<const char*>(memory), file.size());
qDebug() << "映射内容:" << data;
// 解除映射
file.unmap(memory);
}
file.close();
临时文件和缓冲区
临时文件
#include <QTemporaryFile>
#include <QDebug>
QTemporaryFile tempFile;
tempFile.setAutoRemove(true); // 自动删除
if (tempFile.open()) {
// 写入数据
tempFile.write("临时数据");
tempFile.flush();
qDebug() << "临时文件路径:" << tempFile.fileName();
// 读取数据
tempFile.seek(0);
QByteArray data = tempFile.readAll();
qDebug() << "临时文件内容:" << data;
}
// 离开作用域自动删除
内存缓冲区
#include <QBuffer>
#include <QDebug>
QBuffer buffer;
if (buffer.open(QIODevice::ReadWrite)) {
// 写入数据
buffer.write("缓冲区数据");
// 读取数据
buffer.seek(0);
QByteArray data = buffer.readAll();
qDebug() << "缓冲区内容:" << data;
// 获取缓冲区数据
QByteArray rawData = buffer.data();
}
文件监视
#include <QFileSystemWatcher>
#include <QDebug>
class FileWatcher : public QObject {
Q_OBJECT
public:
FileWatcher(QObject* parent = nullptr)
: QObject(parent)
{
connect(&watcher, &QFileSystemWatcher::fileChanged,
this, &FileWatcher::onFileChanged);
connect(&watcher, &QFileSystemWatcher::directoryChanged,
this, &FileWatcher::onDirectoryChanged);
}
void watchFile(const QString& path) {
watcher.addPath(path);
}
void watchDirectory(const QString& path) {
watcher.addPath(path);
}
private slots:
void onFileChanged(const QString& path) {
qDebug() << "文件发生变化:" << path;
}
void onDirectoryChanged(const QString& path) {
qDebug() << "目录发生变化:" << path;
}
private:
QFileSystemWatcher watcher;
};
MFC 文件读写
使用 CFile
文本文件写入(CFile)
#include <afx.h>
CFile file;
if (!file.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::typeText)) {
AfxMessageBox(_T("无法打开文件"));
return;
}
CString content = _T("MFC文件写入测试\n");
file.Write(content, content.GetLength() * sizeof(TCHAR));
file.Close();
写模式参考
CFile 默认二进制模式,加 typeText 切换文本。文本模式组合:
| MFC 组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
modeCreate | modeWrite | typeText | "w" | 覆盖 | 创建或清空后写入 |
modeCreate | modeWrite | modeNoTruncate | typeText | "a" | 追加 | 写入始终追加到末尾 |
modeReadWrite | typeText | "r+" | 定位写入 | 文件必须存在,从当前位置写入 |
modeCreate | modeReadWrite | typeText | "w+" | 覆盖 | 创建或清空后读写 |
modeCreate | modeReadWrite | modeNoTruncate | typeText | "a+" | 追加 | 读可任意位置,写追加到末尾 |
modeCreate会清空已存在文件,加modeNoTruncate转为追加。换typeBinary(或省略,因为二进制是默认)即得对应二进制模式,详见二进制章节。
文本文件读取(CFile)
#include <afx.h>
CFile file;
if (!file.Open(filename, CFile::modeRead | CFile::typeText)) {
AfxMessageBox(_T("无法打开文件"));
return;
}
char buffer[1024];
UINT bytesRead;
while ((bytesRead = file.Read(buffer, sizeof(buffer) - 1)) > 0) {
buffer[bytesRead] = '\0';
// 处理数据
}
file.Close();
读模式参考
文本模式读取(加 typeText):
| MFC 组合 | 等效 C 模式 | 说明 |
|---|---|---|
modeRead | typeText | "r" | 只读,文件必须存在 |
modeReadWrite | typeText | "r+" | 读写,写入行为见写模式参考 |
换
typeBinary(或省略,默认即二进制)得二进制版本,详见二进制章节。modeCreate \| modeReadWrite \| typeText("w+")等也支持读取,详见写模式参考。
二进制文件写入(CFile)
#include <afx.h>
CFile file;
if (file.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::typeBinary)) {
struct Record { int id; char name[50]; double score; };
Record rec = {1, "张三", 85.5};
file.Write(&rec, sizeof(Record));
file.Close();
}
二进制写模式
CFile 默认即二进制(无需 typeBinary,或可显式写出):
| MFC 组合 | 等效 C 模式 | 行为 | 说明 |
|---|---|---|---|
modeCreate | modeWrite | "wb" | 覆盖 | 创建或清空后写入 |
modeCreate | modeWrite | modeNoTruncate | "ab" | 追加 | 写入始终追加到末尾 |
modeReadWrite | "r+b" | 定位写入 | 文件必须存在,从当前位置写入 |
modeCreate | modeReadWrite | "w+b" | 覆盖 | 创建或清空后读写 |
modeCreate | modeReadWrite | modeNoTruncate | "a+b" | 追加 | 读可任意位置,写追加到末尾 |
modeCreate会清空已存在文件,加modeNoTruncate转为追加。加typeText切换文本模式(见文本章节)。
二进制文件读取(CFile)
#include <afx.h>
CFile file;
if (file.Open(filename, CFile::modeRead | CFile::typeBinary)) {
Record rec;
file.Read(&rec, sizeof(Record));
file.Close();
}
二进制读模式
CFile 默认即二进制:
| MFC 组合 | 等效 C 模式 | 说明 |
|---|---|---|
modeRead | "rb" | 只读,文件必须存在 |
modeReadWrite | "r+b" | 读写,写入行为见二进制写模式 |
加
typeText切换文本模式(见文本章节)。modeCreate \| modeReadWrite("w+b")等也支持读取。
打开模式标志速查
| 标志 | 含义 |
|---|---|
CFile::modeRead | 只读 |
CFile::modeWrite | 只写 |
CFile::modeReadWrite | 读写 |
CFile::modeCreate | 创建新文件(已存在且无 modeNoTruncate 则截断) |
CFile::modeNoTruncate | 与 modeCreate 配合使用,不截断已存在的文件 |
CFile::typeText | 文本模式 |
CFile::typeBinary | 二进制模式(默认,与 C/C++ 不同) |
CFile::shareDenyNone | 不拒绝其他进程的读写访问 |
CFile::shareDenyRead | 拒绝其他进程的读取访问 |
CFile::shareDenyWrite | 拒绝其他进程的写入访问 |
CFile::shareExclusive | 独占访问 |
CFile::osNoBuffer | 无系统缓冲 |
CFile::osWriteThrough | 写入直接落盘 |
CFile::osRandomAccess | 随机访问优化 |
CFile::osSequentialScan | 顺序访问优化 |
注意:
CFile默认使用二进制模式,与 C/C++ 标准库默认文本模式不同。写模式组合和写入行为汇总已归入上方「写模式参考」。
使用 CStdioFile
CStdioFile file;
// 写入
if (file.Open(filename, CFile::modeCreate | CFile::modeWrite | CFile::typeText)) {
file.WriteString(_T("第一行内容\n"));
file.WriteString(_T("第二行内容\n"));
file.Close();
}
// 读取
if (file.Open(filename, CFile::modeRead | CFile::typeText)) {
CString line;
while (file.ReadString(line)) {
// 处理每一行
}
file.Close();
}
使用 CArchive(序列化)
// 写入(存储)
{
CFile file(filename, CFile::modeCreate | CFile::modeWrite);
CArchive ar(&file, CArchive::store);
int nValue = 42;
CString str = _T("Hello MFC");
double dValue = 3.14;
ar << nValue << str << dValue;
ar.Close();
file.Close();
}
// 读取(加载)
{
CFile file(filename, CFile::modeRead);
CArchive ar(&file, CArchive::load);
int nValue;
CString str;
double dValue;
ar >> nValue >> str >> dValue;
ar.Close();
file.Close();
}
方案对比与选择建议
| 方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| C FILE* | 性能高、兼容性好 | 不安全、需手动管理资源 | 遗留代码、性能敏感场景 |
| C++ ifstream/ofstream | 类型安全、支持RAII、面向对象 | 相对C风格稍慢 | 现代C++项目首选 |
| Qt QFile | 跨平台、支持信号槽、功能丰富 | 依赖Qt库 | Qt项目首选 |
| QDataStream | 支持Qt类型、版本兼容 | Qt专属格式 | Qt对象序列化 |
| MFC CFile | Windows原生、与MFC框架深度集成 | 仅限Windows、较老旧 | MFC/Windows项目 |
| MFC CArchive | 支持MFC对象序列化 | 仅限Windows、MFC专属格式 | MFC序列化 |
| 内存映射 | 大文件处理快 | 占用虚拟内存 | 大文件读写 |
选择建议
- C项目/性能敏感:使用 C 风格文件操作(
FILE*) - 纯C++项目:使用
std::ifstream/std::ofstream - Qt项目:使用
QFile+QTextStream/QDataStream - MFC项目:使用
CFile/CStdioFile/CArchive - 需要跨平台:优先选择 C++ 标准库或 Qt 的文件操作封装
- 序列化对象:Qt 使用
QDataStream,MFC 使用CArchive - 大文件读写:考虑内存映射
完整示例程序
// main.cpp - 完整示例
#include <QCoreApplication>
#include <QFile>
#include <QTextStream>
#include <QDataStream>
#include <QDebug>
#include <iostream>
#include <fstream>
int main(int argc, char* argv[]) {
QCoreApplication app(argc, argv);
// ========== C++ 标准库示例 ==========
std::cout << "=== C++ 标准库文件操作 ===" << std::endl;
// 写入文本
std::ofstream cppOut("cpp_test.txt");
cppOut << "C++标准库写入测试" << std::endl;
cppOut.close();
// 读取文本
std::ifstream cppIn("cpp_test.txt");
std::string line;
std::getline(cppIn, line);
std::cout << "读取内容: " << line << std::endl;
cppIn.close();
// ========== Qt 文件操作示例 ==========
qDebug() << "\n=== Qt 文件操作 ===";
// 文本文件
QFile qtFile("qt_test.txt");
if (qtFile.open(QIODevice::WriteOnly | QIODevice::Text)) {
QTextStream out(&qtFile);
out << "Qt文件写入测试" << Qt::endl;
qtFile.close();
}
if (qtFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
QTextStream in(&qtFile);
QString content = in.readAll();
qDebug() << "读取内容:" << content;
qtFile.close();
}
// 二进制文件
QFile binFile("qt_binary.dat");
if (binFile.open(QIODevice::WriteOnly)) {
QDataStream out(&binFile);
out << QString("二进制数据") << 42 << 3.14;
binFile.close();
}
qDebug() << "\n所有测试完成!";
return 0;
}
注意事项
编码问题
- Windows下文本文件默认使用本地编码(GBK)
- 建议统一使用 UTF-8 编码
- Qt中使用
QTextStream::setCodec()设置编码
文件路径
- 使用
QDir和QFileInfo处理跨平台路径 - Windows路径分隔符可用
/或\\ - 使用
QDir::separator()获取系统分隔符
错误处理
- 始终检查文件是否成功打开
- 使用
QFile::errorString()获取错误信息 - 大文件操作考虑使用异常处理
何时使用二进制文件读写
应使用二进制模式("rb" / "wb" / std::ios::binary)的场景:
- 存储结构体或对象原始内存数据(
fwrite/write) - 读写图片、音频、视频等非文本格式文件
- 需要精确控制文件字节内容(如网络协议封包、文件格式解析)
- 跨平台交换数据时避免换行符转换(Windows
\r\n↔\n) - 需要随机访问文件指定偏移位置(定长记录)
应使用文本模式("r" / "w" / std::ios::in / std::ios::out)的场景:
- 读写人类可读的配置文件、日志、CSV、JSON 等
- 内容需要在文本编辑器中直接查看或编辑
- 需要平台原生换行符自动转换
注意: Qt 和 MFC 默认二进制模式,与 C/C++ 标准库默认文本模式不同,跨库混用时务必确认模式一致。
性能优化
- 大量小文件写入时,使用缓冲区
- 大文件考虑内存映射
- 频繁读写考虑使用
QBuffer

259

被折叠的 条评论
为什么被折叠?



