Learning Qt 5! (10):文件及其读写

学习文件操作!
Qt 学习之路 2(35):文件
Qt 学习之路 2(36):二进制文件读写
Qt 学习之路 2(37):文本文件读写

一图来看Qt 5中I/O设备的类:
qiodevice
看看Qfile的代码:

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);

    /*这个in.txt文件要自己建,放在项目的build文件夹下,否则会Open file failed.*/
    QFile file("in.txt"); 
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
        qDebug() << "Open file failed.";
        return -1;
    } else {
        while (!file.atEnd()) {
            qDebug() << file.readLine();
        }
    }

    /*有关文件本身的信息,比如文件名、文件所在目录的名字等,是通过QFileInfo获取,而不是
    自己分析文件路径字符串。*/
    QFileInfo info(file);
    qDebug() << info.isDir(); //检查该文件是否是目录
    qDebug() << info.isExecutable(); //检查该文件是否是可执行文件
    qDebug() << info.baseName(); //无路径并第一个.前的名称,此处为"in"
    qDebug() << info.completeBaseName(); //最后一个.前的名称,此处也为"in"
    qDebug() << info.suffix(); //获取文件后缀名,此处为"txt"
    qDebug() << info.completeSuffix(); //第一个.后的后缀,此处也为"txt"

    return app.exec();
}

二进制流的操作用QDataStream,看代码:

	QFile file("file.dat"); //也要先在项目的build文件夹下创建一个file.dat
	file.open(QIODevice::WriteOnly);
	//将刚刚创建的file对象的指针传递给一个QDataStream实例out
	QDataStream out(&file);
	//将“the answer is”和数字 42 输出到数据流
	out << QString("the answer is");
	out << (qint32)42; 
	/*最好使用 Qt 整型来进行读写,比如程序中的qint32。这保证了在任意平台和任意编译器都能够
	有相同的行为。*/
	//为性能起见,数据只有在文件关闭时才会真正写入。因此,我们必须在最后添加一行代码:
	file.close(); // 如果不想关闭文件,可以使用 file.flush();

运行代码后用记事本打开file.dat文件,会看到数字“42”的部分变成了星号*,与ASCII码对应。
再来试试用QDataStream读取file.dat:

	file.open(QIODevice::ReadOnly);
	QDataStream in(&file);
	QString str;
	qint32 a;
	in >> str >> a;
	qDebug() << str << a;

唯一需要注意的是,你必须按照写入的顺序,将数据读取出来。也就是说,程序数据写入的顺序必须预先定义好。在这个例子中,我们首先写入字符串,然后写入数字,那么就首先读出来的就是字符串,然后才是数字。顺序颠倒的话,程序行为是不确定的,严重时会直接造成程序崩溃。

由于二进制流是纯粹的字节数据,带来的问题是,如果程序不同版本之间按照不同的方式读取(前面说过,Qt 保证读写内容的一致,但是并不能保证不同 Qt 版本之间的一致),数据就会出现错误。因此,我们必须提供一种机制来确保不同版本之间的一致性。通常,我们会使用如下的代码写入:

	QFile file("file.dat");
	file.open(QIODevice::WriteOnly);
	QDataStream out(&file);
	
	// 写入魔术数字和版本
	/*所谓魔术数字,是二进制输出中经常使用的一种技术。二进制格式是人不可读的,并且通常具有相同
	的后缀名(比如 dat 之类),因此我们没有办法区分两个二进制文件哪个是合法的。所以,我们定
	义的二进制格式通常具有一个魔术数字,用于标识文件的合法性。在本例中,我们在文件最开始写入
	0xA0B0C0D0,在读取的时候首先检查这个数字是不是0xA0B0C0D0。如果不是的话,说明这个文件不
	是可识别格式,因此根本不需要去继续读取。魔术数字是一个 32 位的无符号整型,因此我们使用
	quint32来得到一个平台无关的 32 位无符号整型。*/
	out << (quint32)0xA0B0C0D0;
	/*文件的不同版本之间也可能存在差异:我们可能在第一版保存整型,第二版可能保存字符串。为了
	标识不同的版本,我们只能将版本写入文件。比如,现在我们的版本是 123。*/
	out << (qint32)123;
	/*Qt不同版本之间的读取方式可能也不一样。这样,我们就得指定Qt按照哪个版本去读。这里,我们
	指定以Qt 4.0格式去读取内容。*/
	out.setVersion(QDataStream::Qt_4_0);

// 写入数据
out << lots_of_interesting_data;

file.close();

加入了魔术数字和版本后,读取文件时也要做相应的判断:

	QFile file("file.dat");
	file.open(QIODevice::ReadOnly);
	QDataStream in(&file);
	
	// 检查魔术数字
	quint32 magic;
	in >> magic;
	if (magic != 0xA0B0C0D0) {
	    return BAD_FILE_FORMAT;
	}
	
	// 检查版本
	qint32 version;
	in >> version;
	if (version < 100) {
	    return BAD_FILE_TOO_OLD;
	}
	if (version > 123) {
	    return BAD_FILE_TOO_NEW;
	}
	
	if (version <= 110) {
	    in.setVersion(QDataStream::Qt_3_2);
	} else {
	    in.setVersion(QDataStream::Qt_4_0);
	}
	// 读取数据
	in >> lots_of_interesting_data;
	if (version >= 120) {
	    in >> data_new_in_version_1_2;
	}
	in >> other_interesting_data;

QDataStream同QIODevice的区别在于,QDataStream提供流的形式,性能上一般比直接调用原始 API 更好一些。通过下面一段代码看看什么是流的形式:

    QFile file("file.dat");
    file.open(QIODevice::ReadWrite);

    QDataStream stream(&file);
    QString str = "the answer is 42";
    QString strout;

    stream << str;
    file.flush();
    /*所谓流,就像水流一样,它的游标会随着输出向后移动。当使用<<操作符输出之后,流的游标
    已经到了最后,此时你再去读,当然什么也读不到了。所以你需要在输出之后重新把游标设置为
    0的位置才能够继续读取。*/
    stream.device()->seek(0); //游标设置为0
    stream >> strout;
    qDebug() << strout;

文本文件的读取用QTextStream,看看代码:

	QFile data("file.txt");
	/*QIODevice::Truncate是以重写的方式打开,在写入新的数据时会将原有数据全部清除,游标
	设置在文件开头。*/
	if (data.open(QFile::WriteOnly | QIODevice::Truncate)) { 
    	QTextStream out(&data);
    	out << "The answer is " << 42;
	}
    data.close();

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

注意我们这里使用的是 >> 重定向,这个函数会以空白字符作为分隔符。因此读取时str只能读出“The”,如果再用一个QString,再读出一个“answer“。

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

猜你喜欢

转载自blog.csdn.net/u013213111/article/details/86141353