Qt文件读写

来源:互联网 发布:recuva数据恢复 编辑:程序博客网 时间:2024/05/21 06:58
今天开始进入 Qt 的另一个部分:文件读写,也就是 IO。文件读写在很多应用程序中都是需要的。Qt 通过 QIODevice 提供了IO的抽象,这种设备(device)具有读写字节块的能力。常用的IO读写的类包括以下几个:
QFlie访问本地文件系统或者嵌入资源QTemporaryFile创建和访问本地文件系统的临时文件QBuffer读写 QByteArrayQProcess运行外部程序,处理进程间通讯QTcpSocketTCP 协议网络数据传输QUdpSocket传输 UDP 报文QSslSocket使用 SSL/TLS 传输数据
 QProcess、QTcpSocket、QUdpSoctet 和 QSslSocket 是顺序访问设备,它们的数据只能访问一遍,也就是说,你只能从第一个字节开始访问,直到最后一个字节。QFile、QTemporaryFile 和 QBuffer 是随机访问设备,你可以从任何位置访问任意次数,还可以使用 QIODevice::seek() 函数来重新定位文件指针。
在访问方式上,Qt 提供了两个更高级别的抽象:使用 QDataStream 进行二进制方式的访问和使用 QTextStream 进行文本方式的访问。这些类可以帮助我们控制字节顺序和文本编码,使程序员从这种问题中解脱出来。
QFile 对于访问独立的文件是非常方便的,无论是在文件系统中还是在应用程序的资源文件中。Qt 同样也提供了 QDir 和 QFileInfo 两个类,用于处理文件夹相关事务以及查看文件信息等。
这次我们先从二进制文件的读写说起。
以二进制格式访问数据的最简单的方式是实例化一个 QFile 对象,打开文件,然后使用 QDataStream 进行访问。QDataStream 提供了平台独立的访问数据格式的方法,这些数据格式包括标准的 C++ 类型,如 int、double等;多种 Qt 类型,如QByteArray、QFont、QImage、QPixmap、QString 和 QVariant,以及 Qt 的容器类,如 QList<T> 和 QMap<K, T>。先看如下的代码:
  1. QImage image("philip.png");  
  2.  
  3. QMap<QString, QColor> map;  
  4. map.insert("red", Qt::red);  
  5. map.insert("green", Qt::green);  
  6. map.insert("blue", Qt::blue);  
  7.  
  8. QFile file("facts.dat");  
  9. if (!file.open(QIODevice::WriteOnly)) {  
  10.     std::cerr << "Cannot open file for writing: " 
  11.               << qPrintable(file.errorString()) << std::endl;  
  12.     return;  
  13. }  
  14.  
  15. QDataStream out(&file);  
  16. out.setVersion(QDataStream::Qt_4_3);  
  17.  
  18. out << quint32(0x12345678) << image << map; 
这里,我们首先创建了一个 QImage 对象,一个 QMap<QString, QColor>,然后使用 QFile 创建了一个名为 "facts.dat" 的文件,然后以只写方式打开。如果打开失败,直接 return;否则我们使用 QFile 的指针创建一个 QDataStream 对象,然后设置 version,这个我们以后再详细说明,最后就像 std 的 cout 一样,使用 << 运算符输出结果。
0x12345678 成为“魔术数字”,这是二进制文件输出中经常使用的一种技术。我们定义的二进制格式通常具有一个这样的“魔术数字”,用于标志文件格式。例如,我们在文件最开始写入 0x12345678,在读取的时候首先检查这个数字是不是 0x12345678,如果不是的话,这就不是可识别格式,因此根本不需要去读取。一般二进制格式都会有这么一个魔术数字,例如 Java 的 class 文件的魔术数字就是 0xCAFE BABE(很 Java 的名字),使用二进制查看器就可以查看。魔术数字是一个 32 位的无符号整数,因此我们使用 quint32 宏来得到一个平台无关的 32 位无符号整数。
在这段代码中我们使用了一个 qPrintable() 宏,这个宏实际上是把 QString 对象转换成 const char *。注意到我们使用的是 C++ 标准错误输出 cerr,因此必须使用这个转换。当然,QString::toStdString() 函数也能够完成同样的操作。
读取的过程就很简单了,需要注意的是读取必须同写入的过程一一对应,即第一个写入 quint32 型的魔术数字,那么第一个读出的也必须是一个 quint32 格式的数据,如
  1. quint32 n;  
  2. QImage image;  
  3. QMap<QString, QColor> map;  
  4.  
  5. QFile file("facts.dat");  
  6. if (!file.open(QIODevice::ReadOnly)) {  
  7.     std::cerr << "Cannot open file for reading: " 
  8.               << qPrintable(file.errorString()) << std::endl;  
  9.     return;  
  10. }  
  11.  
  12. QDataStream in(&file);  
  13. in.setVersion(QDataStream::Qt_4_3);  
  14.  
  15. in >> n >> image >> map; 
好了,数据读出了,拿着到处去用吧!
这个 version 是干什么用的呢?对于二进制的读写,随着 Qt 的版本升级,可能相同的内容有了不同的读写方式,比如可能由大端写入变成了小端写入等,这样的话旧版本 Qt 写入的内容就不能正确的读出,因此需要设定一个版本号。比如这里我们使用 QDataStream::Qt_4_3,意思是,我们使用 Qt 4.3 的方式写入数据。实际上,现在的最高版本号已经是 QDataStream::Qt_4_6。如果这么写,就是说,4.3 版本之前的 Qt 是不能保证正确读写文件内容的。那么,问题就来了:我们以硬编码的方式写入这个 version,岂不是不能使用最新版的 Qt 的读写了?
解决方法之一是,我们不仅仅写入一个魔术数字,同时写入这个文件的版本。例如:
  1. QFile file("file.xxx");  
  2. file.open(QIODevice::WriteOnly);  
  3. QDataStream out(&file);  
  4.  
  5. // Write a header with a "magic number" and a version  
  6. out << (quint32)0xA0B0C0D0;  
  7. out << (qint32)123;  
  8.  
  9. out.setVersion(QDataStream::Qt_4_0);  
  10.  
  11. // Write the data  
  12. out << lots_of_interesting_data; 
这个 file.xxx 文件的版本号是 123。我们认为,如果版本号是123的话,则可以使用 Qt_4_0 版本读取。所以我们的读取代码就需要判断一下:
  1. QFile file("file.xxx");  
  2.  file.open(QIODevice::ReadOnly);  
  3.  QDataStream in(&file);  
  4.  
  5.  // Read and check the header  
  6.  quint32 magic;  
  7.  in >> magic;  
  8.  if (magic != 0xA0B0C0D0)  
  9.      return XXX_BAD_FILE_FORMAT;  
  10.  
  11.  // Read the version  
  12.  qint32 version;  
  13.  in >> version;  
  14.  if (version < 100)  
  15.      return XXX_BAD_FILE_TOO_OLD;  
  16.  if (version > 123)  
  17.      return XXX_BAD_FILE_TOO_NEW;  
  18.  
  19.  if (version <= 110)  
  20.      in.setVersion(QDataStream::Qt_3_2);  
  21.  else 
  22.      in.setVersion(QDataStream::Qt_4_0);  
  23.  
  24.  // Read the data  
  25.  in >> lots_of_interesting_data;  
  26.  if (version >= 120)  
  27.      in >> data_new_in_XXX_version_1_2;  
  28.  in >> other_interesting_data; 
这样,我们就可以比较完美的处理二进制格式的数据读写了。


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

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

QTextStream同QDataStream使用基本一致,例如下面的代码将把“Thomas M. Disch: 334/n”写入到 tmp.txt 文件中:

  1. QFile file("sf-book.txt");  
  2. if (!file.open(QIODevice::WriteOnly)) {  
  3.     std::cerr << "Cannot open file for writing: " 
  4.               << qPrintable(file.errorString()) << std::endl;  
  5.     return;  
  6. }  
  7.  
  8. QTextStream out(&file);  
  9. out << "Thomas M. Disch: " << 334 << endl; 

可以看到,这段代码同前面的 QDataStream 相关代码基本雷同。文本文件的写入比较容易,但是读出就不那么简单了。例如,

  1. out << "Denmark" << "Norway"

是我们写入的代码。我们分别写入两个单词,然后试图以与二进制文件读出的格式相同的形式读出:

  1. in >> str1 >> str2; 

上面两段代码的 out 和 in 都是 QTextStream 类型的。虽然我们可以正常写入,但读出的时候,str1里面将是 DenmarkNorway,str2 是空的。以文本形式写入数据,是不能区分数据的截断位置的。因为使用 QDataStream写入的时候,实际上是要在字符串前面写如长度信息的。因此,对于文本文件,更多的是一种全局性质的操作,比如使用 QTextStream::readLine() 读取一行,使用 QTextStream::readAll() 读取所有文本,之后再对获得的QString对象进行处理。

默认情况下,QTextStream 使用操作系统的本地编码进行读写。不过你可以使用 setCodec() 函数进行设置,比如

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

同 <iostream> 类似,QTextStream 也提供了一些用于格式化输出的描述符,称为stream manipulators。这些描述符放置在输出内容之前,或者是使用相应的函数,用于对后面的输出内容做格式化。具体的描述符如下

setIntegerBase(int)0读出时自动检测数字前缀2二进制8八进制10十进制16十六进制

setNumberFlags(NumberFlags)ShowBase显示前缀,二进制显示0b,八进制显示0,十六进制显示0xForceSign在实数前面显示符号ForcePoint在数字中显示点分隔符UppercaseBase使用大写的前缀,如0B, 0XUppercaseDigits使用大写字母做十六进制数字

setRealNumberNotation(RealNumberNotation)FixedNotation定点计数表示,如0.000123ScientificNotation科学计数法表示,如1.23e-4SmartNotation定点或科学计数法表示,自动选择简洁的一种表示法

setRealNumberPrecision(int)设置生成的最大的小数位数,默认是6

setFieldWidth(int)设置一个字段的最小值,默认是0

setFieldAlignment(FieldAlignment)AlignLeft左对齐AlignRight右对齐AlignCenter中间对齐AlignAccountingStyle符号和数字之间对齐

setPadChar(QChar)设置对齐时填充的字符,默认是空格

比如,下面的代码

  1. out << showbase << uppercasedigits << hex << 12345678; 

将输出0xBC614E。或者我们可以这样去写:

  1. out.setNumberFlags(QTextStream::ShowBase | QTextStream::UppercaseDigits);  
  2. out.setIntegerBase(16);  
  3. out << 12345678; 

QTextStream 不仅仅可以输出到 QIODevice 上,也可以输出到 QString 上面,例如

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

本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/293892

0 0
原创粉丝点击