3. QTextStream文本流:高效读写文本,处理编码与格式化输出
文本读写,说白了就是程序跟文件"聊天"。但怎么聊得顺畅、聊得不出错,这里头门道不少。我个人习惯用QTextStream来处理文本,它比直接操作QFile要优雅得多。
3.1 QTextStream是什么?
QTextStream是Qt提供的一个文本流包装器。它不直接读写文件,而是给QFile、QByteArray这些底层IO设备套上一层"文本层"。你想想看,直接读写二进制数据多麻烦?QTextStream帮你搞定编码转换、格式化输出,省心不少。
核心特点:
- 自动处理字符编码(UTF-8、GBK、Latin1等)
- 支持格式化输出(类似C++的iostream)
- 读写操作简单直观,代码量少
- 内部有缓冲区,性能比逐字符读写好
我在项目中遇到过一个问题:用QFile直接读中文文本,结果全是乱码。后来换成QTextStream,指定了编码格式,问题就解决了。嗯,这里要注意,编码问题往往是新手最容易踩的坑。
3.2 基本用法:打开文件与读写
先看一个最简单的例子——写文件:
QFile file("example.txt");
if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
qWarning() << "文件打开失败";
return;
}
QTextStream out(&file);
out << "Hello, Qt!" << Qt::endl;
out << "这是第二行" << Qt::endl;
file.close();
读文件也差不多:
QFile file("example.txt");
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
qWarning() << "文件打开失败";
return;
}
QTextStream in(&file);
QString line;
while (!in.atEnd()) {
line = in.readLine();
qDebug() << line;
}
file.close();
你看,代码很简洁吧?QIODevice::Text这个标志很重要,它告诉Qt我们要以文本模式操作文件。在Windows上,它会自动处理换行符(\n转\r\n)。
3.3 编码处理:告别乱码
编码问题,说白了就是"同一个字节序列,不同的人理解成不同的字"。QTextStream默认使用UTF-8编码,但你可以随时切换。
QTextStream stream(&file);
// 设置编码为UTF-8
stream.setEncoding(QStringConverter::Utf8);
// 设置编码为GBK(简体中文Windows常用)
stream.setEncoding(QStringConverter::System);
// 设置编码为Latin1
stream.setEncoding(QStringConverter::Latin1);
我曾经接手过一个老项目,配置文件全是GBK编码的。当时我直接用QTextStream读,结果界面上全是"锟斤拷"——懂的人都知道这梗。后来加上setEncoding(QStringConverter::System),世界清净了。
注意:Qt 6中,编码设置方式变了。老版本用setCodec(),Qt 6改用setEncoding()。如果你还在用Qt 5,记得用QTextCodec那一套。
3.4 格式化输出:像cout一样方便
QTextStream支持类似C++ iostream的格式化操作符。我个人觉得这比用QString::arg()拼接字符串要直观得多。
int age = 25;
double salary = 12345.6789;
QString name = "张三";
QTextStream out(&file);
out << "姓名:" << name << Qt::endl;
out << "年龄:" << age << Qt::endl;
// 控制浮点数精度
out << fixed << qSetRealNumberPrecision(2);
out << "薪资:" << salary << "元" << Qt::endl;
// 设置字段宽度和对齐
out << left << qSetFieldWidth(10) << "项目" << qSetFieldWidth(0) << Qt::endl;
out << left << qSetFieldWidth(10) << "数值" << qSetFieldWidth(0) << Qt::endl;
输出效果:
姓名:张三
年龄:25
薪资:12345.68元
项目
数值
小技巧:用Qt::endl而不是"\n"。前者会刷新缓冲区,后者不会。在写日志文件时,我建议用Qt::endl确保数据及时写入磁盘。
3.5 常用操作符一览
| 操作符 | 作用 | 示例 |
|---|---|---|
Qt::endl |
换行并刷新缓冲区 | out << "hello" << Qt::endl; |
ws |
跳过空白字符(读模式) | in >> ws; |
bin / dec / hex |
设置整数进制 | out << hex << 255; |
fixed / scientific |
浮点数格式 | out << fixed << 3.14; |
qSetFieldWidth(n) |
设置字段宽度 | out << qSetFieldWidth(8) << "Qt"; |
left / right / center |
对齐方式 | out << left << "text"; |
3.6 实战:读写CSV文件
CSV文件在项目中很常见。我建议用QTextStream逐行读取,然后按逗号拆分。但要注意,CSV里可能有引号包裹的字段,里面还带逗号——这就要小心了。
// 写CSV
QFile file("data.csv");
file.open(QIODevice::WriteOnly | QIODevice::Text);
QTextStream out(&file);
// 写表头
out << "姓名,年龄,城市" << Qt::endl;
// 写数据行
out << "张三,25,北京" << Qt::endl;
out << "李四,30,上海" << Qt::endl;
out << "\"王,五\",28,\"深圳,广东\"" << Qt::endl;
file.close();
// 读CSV
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine();
QStringList fields = line.split(",");
qDebug() << fields;
}
file.close();
注意:上面的CSV解析是简化版。实际项目中,如果字段里包含逗号或引号,需要用更完善的解析逻辑。我曾经因为偷懒直接用split(","),结果数据错位,排查了半天才发现是某个字段里有个逗号。
3.7 性能建议
QTextStream内部有缓冲区,默认大小是16KB。对于大多数场景,这个值够用了。但如果你要处理超大文件(几百MB以上),我建议:
- 不要一次性读入内存,用
readLine()逐行处理 - 写文件时,可以适当增大缓冲区:
stream.setBufferSize(64 * 1024); - 避免频繁的编码转换,尽量统一编码
说白了,QTextStream就是给文本操作加了一层"舒适层"。它不追求极致性能,但胜在方便、安全、不易出错。我个人在90%的文本读写场景下都用它,剩下的10%——比如要处理二进制文件,那就老老实实用QDataStream或者直接操作QFile吧。
总结一下:QTextStream = QFile + 编码处理 + 格式化输出。记住这三样,文本读写基本就稳了。
可可精品资料库