[经验] Qt学习之路第37篇 文本文件读写

兰博   2018-4-24 13:36 楼主

上一章我们介绍了有关二进制文件的读写。二进制文件比较小巧,却不是人可读的格式。而文本文件是一种人可读的文件。为了操作这种文件,我们需要使用 QTextStream 类。QTextStream 和 QDataStream 的使用类似,只不过它是操作纯文本文件的。另外,像 XML、HTML 这种,虽然也是文本文件,可以由 QTextStream 生成,但 Qt 提供了更方便的 XML 操作类,这里就不包括这部分内容了。


QTextStream 会自动将 Unicode 编码同操作系统的编码进行转换,这一操作对开发人员是透明的。它也会将换行符进行转换,同样不需要自己处理。QTextStream 使用 16 位的 QChar 作为基础的数据存储单位,同样,它也支持 C++ 标准类型,如 int 等。实际上,这是将这种标准类型与字符串进行了相互转换。


QTextStream 同 QDataStream 的使用基本一致,例如下面的代码将把“The answer is 42”写入到 file.txt 文件中:

  • QFile data("file.txt");
  • if (data.open(QFile::WriteOnly | QIODevice::Truncate)) {
  •     QTextStream out(&data);
  •     out << "The answer is " << 42;
  • }

[color=rgb(0, 204, 255) !important]复制代码

这里,我们在 open() 函数中增加了 QIODevice::Truncate 打开方式。我们可以从下表中看到这些打开方式的区别:



枚举值描述
QIODevice::NotOpen未打开
QIODevice::ReadOnly以只读方式打开
QIODevice::WriteOnly以只写方式打开
QIODevice::ReadWrite以读写方式打开
QIODevice::Append以追加的方式打开,新增加的内容将被追加到文件末尾
QIODevice::Truncate以重写的方式打开,在写入新的数据时会将游标设置在文件开头
QIODevice::Text在读取时,将行结束符转换成 \n;在写入时,将行结束符转换成本地格式,例如 Win32 平台上是 \r\n
QIODevice::Unbuffered忽略缓存

我们在这里使用了 QFile::WriteOnly | QIODevice::Truncate,也就是以只写并且覆盖已有内容的形式操作文件。注意,QIODevice::Truncate 并不是将文件内容清空,而是在文件开头处开始写入。要理解这一点,假设原文件内容是1111111111,我们想要写入222,如果设置为 QIODevice::Append,则处理结果将会是 1111111111222,这是很容易理解的;如果设置为 QIODevice::Truncate,那么文件内容将会是 2221111111。



虽然 QTextStream 的写入内容与 QDataStream 一直,但是读取时却会有些困难:

  • QFile data("file.txt");
  • if (data.open(QFile::ReadOnly)) {
  •     QTextStream in(&data);
  •     QString str;
  •     int ans = 0;
  •     in >> str >> ans;
  • }

[color=rgb(0, 204, 255) !important]复制代码

在使用 QDataStream 的时候,这样的代码很方便,但是使用了 QTextStream 时却有所不同:读出的时候,str 里面将是 The answer is 42,ans 是 0。这是因为以文本形式写入数据,是没有数据之间的分隔的。还记得我们前面曾经说过,使用 QDataStream 写入的时候,实际上会在要写入的内容前面,额外添加一个这段内容的长度值。而文本文件则没有类似的操作。因此,使用文本文件时,很少会将其分割开来读取,而是使用诸如 QTextStream::readLine() 读取一行,使用 QTextStream::readAll() 读取所有文本这种函数,之后再对获得的 QString 对象进行处理。



默认情况下,QTextStream 的编码格式是 Unicode,如果我们需要使用另外的编码,可以使用

  • stream.setCodec("UTF-8");

[color=rgb(0, 204, 255) !important]复制代码

这样的函数进行设置。



另外,为方便起见,QTextStream 同 std::cout 一样提供了很多描述符,被称为 stream manipulators。因为文本文件是供人去读的,自然需要良好的格式(相比而言,二进制文件就没有这些问题,只要数据准确就可以了)。这些描述符是一些函数的简写,我们可以从文档中找到:



描述符等价于
binsetIntegerBase(2)
octsetIntegerBase(8)
decsetIntegerBase(10)
hexsetIntegerBase(16)
showbasesetNumberFlags(numberFlags() | ShowBase)
forcesignsetNumberFlags(numberFlags() | ForceSign)
forcepointsetNumberFlags(numberFlags() | ForcePoint)
noshowbasesetNumberFlags(numberFlags() & ~ShowBase)
noforcesignsetNumberFlags(numberFlags() & ~ForceSign)
noforcepointsetNumberFlags(numberFlags() & ~ForcePoint)
uppercasebasesetNumberFlags(numberFlags() | UppercaseBase)
uppercasedigitssetNumberFlags(numberFlags() | UppercaseDigits)
lowercasebasesetNumberFlags(numberFlags() & ~UppercaseBase)
lowercasedigitssetNumberFlags(numberFlags() & ~UppercaseDigits)
fixedsetRealNumberNotation(FixedNotation)
scientificsetRealNumberNotation(ScientificNotation)
leftsetFieldAlignment(AlignLeft)
rightsetFieldAlignment(AlignRight)
centersetFieldAlignment(AlignCenter)
endloperator<<(‘\n’) and flush()
flushflush()
resetreset()
wsskipWhiteSpace()
bomsetGenerateByteOrderMark(true)

这些描述符只是一些函数的简写。例如,我们想要输出 12345678 的二进制形式,那么可以直接使用

  • out << bin << 12345678;

[color=rgb(0, 204, 255) !important]复制代码

就可以了。这等价于

  • out.setIntegerBase(2);
  • out << 12345678;

[color=rgb(0, 204, 255) !important]复制代码

更复杂的,如果我们想要舒服 1234567890 的带有前缀、全部字母大写的十六进制格式(0xBC614E),那么只要使用

  • out << showbase << uppercasedigits << hex << 12345678;

[color=rgb(0, 204, 255) !important]复制代码

即可。



不仅是 QIODevice,QTextStream 也可以直接把内容输出到 QString。例如

  • QString str;
  • QTextStream(&str) << oct << 31 << " " << dec << 25 << endl;

[color=rgb(0, 204, 255) !important]复制代码

这提供了一种简单的处理字符串内容的方法。


回复评论 (1)

建议每次都能展示软件运行效果?
点赞  2018-4-29 10:30
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复