首页 Qt 学习之路 2 Qt 学习之路 2(37):文本文件读写

Qt 学习之路 2(37):文本文件读写

24 5K

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

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

QTextStreamQDataStream的使用基本一致,例如下面的代码将把“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;
}

这里,我们在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会直接将文件内容清空。

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

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

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

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

stream.setCodec("UTF-8");

这样的函数进行设置。

另外,为方便起见,QTextStreamstd::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')flush()
flushflush()
resetreset()
wsskipWhiteSpace()
bomsetGenerateByteOrderMark(true)

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

out << bin << 12345678;

就可以了。这等价于

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

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

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

即可。

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

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

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

24 评论

424778940 2013年4月30日 - 22:57

想请教些问题,我的以下代码
QFile rw_file;
rw_file.setFileName(rw_file_path);
rw_file.open(rw_file.WriteOnly);
rw_file.seek(pos);

QDataStream rw_file_stream(&rw_file);
rw_file_stream.setVersion(rw_file_stream.Qt_5_0);
rw_file_stream.setByteOrder(rw_file_stream.LittleEndian);
rw_file_stream << data_u32;

rw_file.close();

目的是打开一个二进制文件,在特定位置写入数据,覆盖掉该位置原来的数据
但是我明明没有指定QIODevice::Truncate这个选项,写入文件之后源文件内容还是被清空了
指定QIODevice::Truncate的话,也是一样被清空...,源文件内容完全没有了
实在是莫名其妙,qt版本是官网的5.0.2 windows版
希望邮件回复,非常感谢

回复
豆子 2013年5月2日 - 10:55

如果可以的话,请将测试代码发送到我的邮箱 devbean#outlook.com 吧,这样看实在看不出什么问题…

回复
424778940 2013年5月2日 - 14:34

问题已经解决了...WriteOnly换成ReadWrite就好了
但是用WriteOnly会清空原来文件内容这件事官方文档上一个字都没提,真是莫名其妙

回复
Wangzhe 2014年8月22日 - 13:14

C函数里的只写模式就是默认会清空文件内容,Qt可能是为了和C保持一致吧(也许文档里没提是把它当作是一个传统习惯,认为大家都会知道)。

回复
豆子 2014年8月23日 - 12:00

有可能,也算一种惯例吧

Mino 2019年12月17日 - 16:51

实际上官方文档是有的:
“The device is open for writing. Note that, for file-system subclasses (e.g. QFile), this mode implies Truncate unless combined with ReadOnly, Append or NewOnly.”
摘录自QtCreater中的help file

回复
Buf 2016年9月9日 - 10:19

计算器的类函数中,如果没有任何指定,初始化总在一个状态(因为计算代码不是1,就是0),关于你顶一个了rw_file_stream(QDataStream) , 它有默认的设置(瞎说一下:这可能和系统/软件版本号(博主读过原码可能知道)有关)。所以:未初始化的定义,如果实际需要,必要时要重新set。更加指定用户的需求。

回复
R 2013年12月2日 - 10:01

qt5有没有excel或者.ods文件读写

回复
豆子 2013年12月2日 - 16:07

没有。作为通用库,不会提供解析某一类型文件的函数的。不过你可以使用 Qt 调用 ActiveX 操作 excel,或者寻找任何第三方 C++ 库

回复
R 2013年12月2日 - 16:09

谢谢,我去看看怎么用activeX

回复
付强 2014年1月5日 - 19:49

求教一个问题。如果我打开方式 直接写 QIODevice::Append 可行。如果直接写QIODevice::Truncate不可行,运行报:QIODevice::open: File access not specified
我用 QIODevice::WriteOnly | QIODevice::Truncate 不报错了,但是是直接覆盖了。比如我原文件内容是xxxxx:43 接下来我用QIODevice::WriteOnly | QIODevice::Truncate 写43. 结果文本中只剩下43了,而不是43xxx:43;求解?
code:http://paste.ubuntu.com/6696434/

回复
豆子 2014年1月6日 - 21:27

QIODevice::Truncate 不仅可以结合 QIODevice::WriteOnly 使用,也可以结合 QIODevice::ReadOnly。所以在使用 QIODevice::Truncate 的时候必须指定是以哪种方式打开。QIODevice::Truncate 是在设备打开之前将其“截断”,对于读、写都适用。

回复
Const_Lin 2014年3月15日 - 10:15

豆子你好:
空格会影响文本的读取吗?第二个例子中,str只能读出“The”,再用一个QString,再读出一个“answer“

回复
豆子 2014年3月17日 - 09:10

注意我们这里使用的是 >> 重定向,这个函数会以空白字符作为分隔符。

回复
liyongnan 2014年3月21日 - 12:22

豆子高手,出现以下错误:
ambiguous overload for 'operator<<' (operand types are 'QTextStream' and 'QTextStream&(QTextStream&)')
QTextStream(&str) << oct << 31 << " " << dec << 25 << endl;
^

回复
liyongnan 2014年3月21日 - 12:31

好像问题出在 oct 和 dec 上

回复
Edward 2014年4月25日 - 14:57

请教个小问题,输出TXT文件,怎么输出中文呢? out<<"你好"; 出来的txt文件中,“你好”二字是乱码,我用的时QT5.2,非常感谢!

回复
豆子 2014年4月28日 - 10:28

Qt 5 已经不要求指定编码了,注意下你的文件需要是 UTF-8 编码的(Windows 平台默认是 GBK)。并且一般源代码中很少出现中文的,所以可以试着尽量避免中文。

回复
R 2014年7月6日 - 05:53

你好,我是用QT5.2.1, 我想在日志文件里面记录中文内容,一直是乱码,google了好多文章都不行,请教一下需要才可以解决.感谢。

回复
豆子 2014年7月6日 - 09:57

Qt 5 要求源代码文件是 UTF-8 编码的,看看编码是不是正确?

回复
Buf 2016年9月9日 - 10:11

devbean,你好!我是一个初学者,C语言还好,C++自学懂得一些概念。 学习qt本来是为了能写个在PC端与设备之间的通讯,但一步一个脚印,我决定把QT学好。
在这一个章节写文件我有些问题需要问一下:
往文件中中写:QString:the answer is : ...
往文件中写:Qint32:42
结果我在本地用UE打开,看到"the answer is : "
1. 每一个字母之间都多两个空格:
我用二进制形式查看了文件,发现每一个字母占用16bit,我改如何让这段String连续的写入文本呢(每一个字母占用8bit(char)).
2.qint32 用文本形式显示的是“*“,刚好对应ascii的42 。 为什么你在后面提示说该段为“the answer is : 42“ 呢 ? 明显前面是QString,后面是Qint32 。
我用的是QT5.7.0 , 望可解答。谢谢

回复
Buf 2016年9月9日 - 10:32

我找到原因了。 我定义的是QDataSteam out 。 而博主说的是QTextSteam out 。 导致以上两种结果

回复
豆子 2016年9月10日 - 08:26

QDataSteam 是以二进制方式读写;QTextSteam 是文本格式。

回复
小张 2016年11月22日 - 11:50

QFile data("file.txt");
if (data.open(QFile::ReadOnly)) {
QTextStream in(&data);
QString str;
int ans = 0;
in >> str >> ans;
}
用qDebug()输出是以下:
"The" 0

回复

发表评论

关于我

devbean

devbean

豆子,生于山东,定居南京。毕业于山东大学软件工程专业。软件工程师,主要关注于 Qt、Angular 等界面技术。

主题 Salodad 由 PenciDesign 提供 | 静态文件存储由又拍云存储提供 | 苏ICP备13027999号-2