今天开始进入 Qt 的另一个部分:文件读写,也就是 IO。文件读写在很多应用程序中都是需要的。Qt 通过 QIODevice 提供了IO的抽象,这种设备(device)具有读写字节块的能力。常用的IO读写的类包括以下几个:
QFlie 访问本地文件系统或者嵌入资源
QTemporaryFile 创建和访问本地文件系统的临时文件
QBuffer 读写 QByteArray
QProcess 运行外部程序,处理进程间通讯
QTcpSocket TCP 协议网络数据传输
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 和 QMap。先看如下的代码:
  1. QImage image("philip.png");
  2. QMap map;
  3. map.insert("red", Qt::red);
  4. map.insert("green", Qt::green);
  5. map.insert("blue", Qt::blue);
  6. QFile file("facts.dat");
  7. if (!file.open(QIODevice::WriteOnly)) {
  8. std::cerr << "Cannot open file for writing: "
  9. << qPrintable(file.errorString()) << std::endl;
  10. return;
  11. }
  12. QDataStream out(&file);
  13. out.setVersion(QDataStream::Qt_4_3);
  14. out << quint32(0x12345678) << image << map;
这里,我们首先创建了一个 QImage 对象,一个 QMap,然后使用 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 map;
  4. QFile file("facts.dat");
  5. if (!file.open(QIODevice::ReadOnly)) {
  6. std::cerr << "Cannot open file for reading: "
  7. << qPrintable(file.errorString()) << std::endl;
  8. return;
  9. }
  10. QDataStream in(&file);
  11. in.setVersion(QDataStream::Qt_4_3);
  12. 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. // Write a header with a "magic number" and a version
  5. out << (quint32)0xA0B0C0D0;
  6. out << (qint32)123;
  7. out.setVersion(QDataStream::Qt_4_0);
  8. // Write the data
  9. 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. // Read and check the header
  5. quint32 magic;
  6. in >> magic;
  7. if (magic != 0xA0B0C0D0)
  8. return XXX_BAD_FILE_FORMAT;
  9. // Read the version
  10. qint32 version;
  11. in >> version;
  12. if (version < 100)
  13. return XXX_BAD_FILE_TOO_OLD;
  14. if (version > 123)
  15. return XXX_BAD_FILE_TOO_NEW;
  16. if (version <= 110)
  17. in.setVersion(QDataStream::Qt_3_2);
  18. else
  19. in.setVersion(QDataStream::Qt_

http://blog.sina.com.cn/s/blog_a401a1ea0101ffnb.html

Qt 二进制文件读写(使用“魔术数字”)的更多相关文章

  1. Qt 学习之路:二进制文件读写

    在上一章中,我们介绍了有关QFile和QFileInfo两个类的使用.我们提到,QIODevice提供了read().readLine()等基本的操作.同时,Qt 还提供了更高一级的操作:用于二进制的 ...

  2. Qt 学习 之 二进制文件读写

    在上一章中,我们介绍了有关QFile和QFileInfo两个类的使用.我们提到,QIODevice提供了read().readLine()等基本的操作.同时,Qt 还提供了更高一级的操作:用于二进制的 ...

  3. Qt 学习之路 2(36):二进制文件读写

    Qt 学习之路 2(36):二进制文件读写 豆子 2013年1月6日 Qt 学习之路 2 20条评论 在上一章中,我们介绍了有关QFile和QFileInfo两个类的使用.我们提到,QIODevice ...

  4. [转载:]Fortran 二进制文件读写

    一些朋友总是咨询关于二进制文件的读写和转化.这里就我自己的理解说一说. 一).一般问题 二进制文件与我们通常使用的文本文件储存方式有根本的不同.这样的不同很难用言语表达,自己亲自看一看,理解起来会容易 ...

  5. JAVA核心技术I---JAVA基础知识(二进制文件读写和zip文件读写)

    一:二进制文件读写 (一)写文件 –先创建文件,写入数据,关闭文件 –FileOutputStream, BufferedOutputStream,DataOutputStream –DataOutp ...

  6. C++二进制文件读写

    简单二进制文件读写,多文件 /*Demo9.1.cpp*/ #include <iostream> #include <fstream> #include <string ...

  7. LeetCode 5112. 十六进制魔术数字 Hexspeak

    地址 https://leetcode-cn.com/problems/hexspeak/ 题目描述字母大写的十六进制字符串,然后将所有的数字 0 变成字母 O ,将数字 1  变成字母 I . 如果 ...

  8. QT QSettings读写配置文件

    QT QSettings读写配置文件 需要用一个配置文件来保存程序的初始值,同时也需要做保存修改后的值. 那么借助于QSetting 就可以达到目的. 注意,生成的是 ini 文件! //1.创建和读 ...

  9. Java随谈(一)魔术数字、常量和枚举

    本文适合对 Java 或 C 有一些了解的用户阅读,推荐阅读时间15分钟. 导言 写这个系列的原因? 我曾经听过一种说法,如果不了解Liunx的网络通讯,就很难理解理解Java的IO:如果不知道Jav ...

随机推荐

  1. linux ubuntu vsftp 默认主目录

    vi /etc/passwd 查看/ftp:  后面的目录就是默认目录 这是匿名用户的目录 --------------------------- 使用linux 别的用户,默认在/home/用户名 ...

  2. color 颜色代码 android res/values/colors.xml

    <?xml version="1.0" encoding="utf-8"?> <resources> <color name=&q ...

  3. 表格细边框 并且CSS3加圆角

    .YJ table{width:625px;height:860px;text-align:center;overflow:hidden; background:#fff;border-radius: ...

  4. [Interview][CodingExam]

    這次去Interview, 其中有一個公司 把我列為 2/25的考慮對象, 在Final 的 1/2, 我被刷掉了. 因為第一輪的程式,我稍微google了一下,參考了既有的寫法. 即使第二輪我用完全 ...

  5. 九度OJ做题记录 更新.....

    2015年1月7日 20:34:23  题目1007:奥运排序问题 有点意思,以后想另外方法快速做出来 2015年1月7日 21:03:56 有一个技巧就是,写了三个比较函数cmp1,cmp2,cmp ...

  6. WPF处理Windows消息

    WPF中处理消息首先要获取窗口句柄,创建HwndSource对象 通过HwndSource对象添加消息处理回调函数. HwndSource类: 实现其自己的窗口过程. 创建窗口之后使用 AddHook ...

  7. PHP内置函数

    1 变量函数 a.is_xxx函数用来判断变量类型       is_int.is_integer.is_long,判断变量是否是整型       is_float.is_double.is_real ...

  8. go语言实现线程池

    话说真的好久没有写博客了,最近赶新项目,工作太忙了.这一周任务比较少,又可以随便敲敲了. 逛论坛的时候突发奇想,想用go语言实现一个线程池,主要功能是:添加total个任务到线程池中,线程池开启num ...

  9. Linux配置Tomcat(转载)

    转载自:http://www.cnblogs.com/zhoulf/archive/2013/02/04/2891633.html 安装说明 安装环境:CentOS-6.3安装方式:源码安装 软件:a ...

  10. 【单例模式】单例模式 & GCD单例模式 & 将封装单例模式到宏

    懒汉式单例模式 下面的代码块, 基本是单例模式的完整版本了. 可扩展的地方,可以在init方法中作扩展. // static 在全局变量的作用域仅限于当前文件内部 static id _instanc ...