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 + 编码处理 + 格式化输出。记住这三样,文本读写基本就稳了。

QTextStream 知识体系 QTextStream 📄 文件读写 🔤 编码处理 🎨 格式化输出 readLine() / << 操作符 UTF-8 / GBK / Latin1 fixed / hex / 字段宽度 逐行读取大文件 跨平台编码兼容 日志/报表生成 💡 核心:QFile + 编码 + 格式化 = QTextStream 适用于90%的文本读写场景

可可精品资料库