1 文本文件读写
1.1 用QFile读写文本文件
QFile类是直接与IO设备打交道,进行文件读写的类。
[virtual] bool QFile::open(OpenMode mode);//打开文件。并指定模式
enum QIODevice::OpenModeFlag
{
QIODevice::ReadOnly,//只读,用于载入文件
QIODevice::WriteOnly,//只写,用于保存文件
QIODevice::ReadWrite,//读写方式
QIODevice::Append,//添加至尾部
QIODevice::Truncate,//删除全部内容后打开
QIODevice::Text//文本方式
};
[virtual] void QFileDevice::close();//关闭文件,并指定模式
QByteArray QIODevice::readAll();//读出所有文件内容
qint64 QIODevice::readLine(char *data, qint64 maxSize)//读一行,会自动添加 \n!!!!!!!!
qint64 QIODevice::write(const char *data, qint64 maxSize);//写入到文件
//用QFile从文件中读,可一次全部读出或者读一行
QString curPath=QDir::currentPath();//获取系统当前目录
QString dlgTitle="打开一个文件"; //对话框标题
QString filter="程序文件(*.h *.cpp);;文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
QString aFileName=QFileDialog::getOpenFileName(this,dlgTitle,curPath,filter);//创建对话框并指定打开一个文件,返回文件名
if (aFileName.isEmpty())
return;//返回控制,可以等待下一次触发
QFile aFile(aFileName);//创建QFile对象并指定文件名
if(!aFile.exists()) //文件不存在
return false;
if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QByteArray data = aFile.readAll()
/*
while (!aFile.atEnd())
{
QByteArray line = aFile.readLine();//读一行文本,会自动添加 \n!!!!!
QString str=QString::fromLocal8Bit(line); //从字节数组转换为字符串
str.truncate(str.length()-1); //去除结尾增加的\n,便于显示
}
*/
aFile.close();
//用QFile向文件中写,一次全部写入
QString curPath=QDir::currentPath();//获取系统当前目录
QString dlgTitle="另存为一个文件"; //对话框标题
QString filter="h文件(*.h);;c++文件(*.cpp);;文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
QString aFileName=QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);
if (aFileName.isEmpty())
return;
QFile aFile(aFileName);
if (!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QString str=ui->textEditDevice->toPlainText();//整个内容作为字符串
QByteArray strBytes=str.toUtf8();//转换为字节数组
//QByteArray strBytes=str.toLocal8Bit();
aFile.write(strBytes,strBytes.length()); //写入文件
aFile.close();
1.2 用QFile和QTextStream结合读写文本文件
QTextStream可以和QFile、QTemporary、QBuffer、QTcpSocket、QUdpSocket等QIODevice类结合使用
QString QTextStream::readAll();
QString QTextStream::readLine(qint64 maxlen = 0);//读取文件的一行,不会自动添加 \n!!!!!
QTextStream &QTextStream::operator<<(const QString &string);//重载插入运算符,写入QString
void QTextStream::setAutoDetectUnicode(bool enabled);
//用QFile结合QTextStream从文件中读
QString curPath=QDir::currentPath();//获取系统当前目录
QString dlgTitle="另存为一个文件"; //对话框标题
QString filter="h文件(*.h);;c++文件(*.cpp);;文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
QString aFileName=QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);
if (aFileName.isEmpty())
return;
QFile aFile(aFileName);
if (!aFile.exists()) //文件不存在
return false;
if (!aFile.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QTextStream aStream(&aFile); //用文本流读取文件,并绑定文件对象
aStream.setAutoDetectUnicode(true); //自动检测Unicode,才能正常显示文档内的汉字
QString str = aStream.readAll();//一次读取全部内容,返回为QString
/*
while (!aStream.atEnd())
QString str = aStream.readLine();//读取文件的一行,不会自动添加 \n!!!!!
*/
aFile.close();//关闭文件
//用QFile结合QTextStream向文件中写
QString curPath=QDir::currentPath();//获取系统当前目录
QString dlgTitle="另存为一个文件"; //对话框标题
QString filter="h文件(*.h);;c++文件(*.cpp);;文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
QString aFileName=QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);
if (aFileName.isEmpty())
return;
QFile aFile(aFileName);
if (!aFile.open(QIODevice::WriteOnly | QIODevice::Text))
return false;
QTextStream aStream(&aFile); //用文本流读取文件
aStream.setAutoDetectUnicode(true); //自动检测Unicode,才能正常显示文档内的汉字
QString str=ui->textEditStream->toPlainText(); //转换为字符串
aStream << str; //写入文本流
/*
QTextDocument* doc; //文本对象
QTextBlock textLine; //文本中的一段
doc = ui->textEditStream->document(); //QPlainTextEdit 的内容保存在一个 QTextDocument 里
int cnt = doc->blockCount();//QTextDocument分块保存内容,硬回车符是一个block,
QString str;
for (int i = 0; i < cnt; i++) //扫描所有 blobk
{
textLine = doc->findBlockByNumber(i);//用blobk编号获取block,就是获取一行
str = textLine.text(); //转换为文本,末尾无\n
aStream<<str<<"\n";
}
*/
aFile.close();//关闭文件
-
解决中文乱码的问题
用QTextStream读或写有中文的文本文件时,为了能正确识别Unicode码,需要调用setAutoDetectUnicode函数,否则会出现乱码。
我们也可以在应用程序中进行全局的设置,使用UTF-8编码解码器,使得应用程序支持Unicode。
#include <QApplication>
#include <QTextCodec>
int main(int argc, char *argv[])
{
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
QTextCodec::setCodecForLocale(codec); //解决汉字乱码问题
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
2 二进制文件读写QFile和QDataStream
除了文本文件,其它需要按一定格式定义读写的文件称为二进制文件,写入时要按一定格式写入,读出时也要按照这个格式读出。
- Qt使用QFile和QDataStream进行二进制文件的读写,QFile负责文件的IO设备接口,即与文件的物理交互。QDataStream以数据流的方式读取文件内容或写入文件。
- 根据QDataStream保存文件时使用的数据编码的方式不同,可保存为两种文件。一种是用Qt预定义编码保存各种类型的文件,后缀为.stm,每种类型由Qt编码,值与Qt版本有关。另一种是标准编码数据文件,后缀为.dat,写入时完全使用数据的二进制原始内容。
2.1 Qt预定义编码文件stm
下面的代码保存一个表格,保存为stm格式。
//保存为Qt预定义编码文件stm
QString curPath=QDir::currentPath();
QString aFileName = QFileDialog::getSaveFileName(this,tr("选择保存文件"),curPath,"Qt预定义编码数据文件(*.stm)");
if (aFileName.isEmpty())
return;
if (saveDataAsStream(aFileName))
QMessageBox::information(this,"提示消息","文件已经成功保存!");
bool MainWindow::saveDataAsStream(QString &aFileName)
{
QFile aFile(aFileName);//以文件方式读出
if (!(aFile.open(QIODevice::WriteOnly | QIODevice::Truncate)))
return false;
QDataStream aStream(&aFile);
aStream.setVersion(QDataStream::Qt_5_9); //设置版本号,写入和读取的版本号要兼容
qint16 rowCount = theModel->rowCount(); //数据模型行数
qint16 colCount = theModel->columnCount(); //数据模型列数
aStream << rowCount; //写入文件流,行数
aStream << colCount;//写入文件流,列数
//获取表头文字
for (int i = 0;i < theModel->columnCount();i++)
{
QString str = theModel->horizontalHeaderItem(i)->text();//获取表头文字
aStream << str; //字符串写入文件流,Qt预定义编码方式
}
//获取数据区的数据
for (int i = 0;i < theModel->rowCount();i++)
{
QStandardItem* aItem = theModel->item(i,0); //测深
qint16 ceShen = aItem->data(Qt::DisplayRole).toInt();
aStream << ceShen;// 写入文件流,qint16
aItem=theModel->item(i,1); //垂深
qreal chuiShen = aItem->data(Qt::DisplayRole).toFloat();
aStream << chuiShen;//写入文件流, qreal
aItem = theModel->item(i,2); //方位
qreal fangWei = aItem->data(Qt::DisplayRole).toFloat();
aStream << fangWei;//写入文件流, qreal
aItem = theModel->item(i,3); //位移
qreal weiYi = aItem->data(Qt::DisplayRole).toFloat();
aStream << weiYi;//写入文件流, qreal
aItem = theModel->item(i,4); //固井质量
QString zhiLiang = aItem->data(Qt::DisplayRole).toString();
aStream << zhiLiang;// 写入文件流,字符串
aItem = theModel->item(i,5); //测井
bool quYang = (aItem->checkState()==Qt::Checked);
aStream << quYang;// 写入文件流,bool型
}
aFile.close();
return true;
}
//读取Qt预定义编码文件stm
void MainWindow::on_actOpen_triggered()
{
QString curPath=QDir::currentPath();
QString aFileName=QFileDialog::getOpenFileName(this,tr("打开一个文件"),curPath,"流数据文件(*.stm)");
if (aFileName.isEmpty())
return;
if (openDataAsStream(aFileName)) //保存为流数据文件
QMessageBox::information(this,"提示消息","文件已经打开!");
}
bool MainWindow::openDataAsStream(QString &aFileName)
{
QFile aFile(aFileName);//以文件方式读出
if (!(aFile.open(QIODevice::ReadOnly)))
return false;
QDataStream aStream(&aFile); //用文本流读取文件
aStream.setVersion(QDataStream::Qt_5_9); //设置流文件版本号,保存和读取的版本号要相同
qint16 rowCount,colCount;
aStream >> rowCount; //读取行数
aStream >> colCount; //列数
this->resetTable(rowCount); //表格复位
//获取表头文字
QString str;
for (int i = 0;i < colCount;i++)
aStream >> str; //读取表头字符串
//获取数据区文字,
qint16 ceShen;
qreal chuiShen;
qreal fangWei;
qreal weiYi;
QString zhiLiang;
bool quYang;
QStandardItem *aItem;
QModelIndex index;
for (int i = 0;i < rowCount;i++)
{
aStream >> ceShen;//读取测深, qint16
index = theModel->index(i,0);
aItem = theModel->itemFromIndex(index);
aItem->setData(ceShen,Qt::DisplayRole);
aStream >> chuiShen;//垂深,qreal
index = theModel->index(i,1);
aItem = theModel->itemFromIndex(index);
aItem->setData(chuiShen,Qt::DisplayRole);
aStream >> fangWei;//方位,qreal
index = theModel->index(i,2);
aItem = theModel->itemFromIndex(index);
aItem->setData(fangWei,Qt::DisplayRole);
aStream >> weiYi;//位移,qreal
index = theModel->index(i,3);
aItem = theModel->itemFromIndex(index);
aItem->setData(weiYi,Qt::DisplayRole);
aStream>>zhiLiang;//固井质量,QString
index=theModel->index(i,4);
aItem=theModel->itemFromIndex(index);
aItem->setData(zhiLiang,Qt::DisplayRole);
aStream >> quYang;//bool
index=theModel->index(i,5);
aItem = theModel->itemFromIndex(index);
if (quYang)
aItem->setCheckState(Qt::Checked);
else
aItem->setCheckState(Qt::Unchecked);
}
aFile.close();
return true;
}
- 用这种方式读写操作都很方便,特别是对于Qt的一些内置类型。
- 但是拓展性不强,其它的工具无法读取,因为它无法解析Qt的编码,只有Qt才可以。
2.2 标准编码文件dat
- 字节序问题
字节序分为小端字节序和大端字节序,小端字节序低字节数据放在内存低地址处,大端字节序低字节数据放在内存高地址处。X86计算机都是小端字节序的,一些嵌入式平台使用的arm计算机是大端字节序的。读出文件时,我们要指定它的字节序,这样才能正确读取。写入文件时,我们也要注意字节序的设置。在跨平台使用dat文件时尤其需要注意!!!!
int QDataStream::writeRawData(const char *s, int len);
int QDataStream::readRawData(char *s, int len);
QDataStream &QDataStream::writeBytes(const char *s, uint len);
QDataStream &QDataStream::readBytes(char *&s, uint &len);
- writeBytes函数适用于写入字符串数据,在写入数据时,它会先写入len值quint32,表示字符串的长度,接着再写入len个从指针s获得的数据。这样在读取时,readBytes函数就会自动读取长度len,从而避免了字符串长度一般不定的问题。
- 注意写出和读入的函数要匹配!!!!
//保存为标准编码文件dat
QString curPath=QDir::currentPath();
QString aFileName=QFileDialog::getSaveFileName(this,tr("选择保存文件"),curPath,"二进制数据文件(*.dat)");
if (aFileName.isEmpty())
return;
if (saveBinaryFile(aFileName))
QMessageBox::information(this,"提示消息","文件已经成功保存!");
bool MainWindow::saveBinaryFile(QString &aFileName)
{
QFile aFile(aFileName);
if (!(aFile.open(QIODevice::WriteOnly)))
return false;
QDataStream aStream(&aFile); //用文本流读取文件
aStream.setByteOrder(QDataStream::LittleEndian);//windows平台,保存为小端格式
qint16 rowCount = theModel->rowCount();
qint16 colCount = theModel->columnCount();
aStream.writeRawData((char *)&rowCount,sizeof(qint16));
aStream.writeRawData((char *)&colCount,sizeof(qint16));
//获取表头文字
QByteArray btArray;
QStandardItem *aItem;
for (int i = 0;i < theModel->columnCount();i++)
{
aItem = theModel->horizontalHeaderItem(i); //获取表头item
QString str = aItem->text(); //获取表头文字
btArray = str.toUtf8(); //转换为字符数组
aStream.writeBytes(btArray,btArray.length()); //写入文件流,长度uint型,然后是字符串内容
}
//获取数据区文字
qint8 yes = 1,no = 0; //分别代表逻辑值 true和false
for (int i = 0;i < theModel->rowCount();i++)
{
aItem = theModel->item(i,0); //测深
qint16 ceShen=aItem->data(Qt::DisplayRole).toInt();//qint16类型
aStream.writeRawData((char *)&ceShen,sizeof(qint16));//写入文件流
aItem=theModel->item(i,1); //垂深
qreal chuiShen = aItem->data(Qt::DisplayRole).toFloat();//qreal 类型
aStream.writeRawData((char *)&chuiShen,sizeof(qreal));//写入文件流
aItem=theModel->item(i,2); //方位
qreal fangWei=aItem->data(Qt::DisplayRole).toFloat();
aStream.writeRawData((char *)&fangWei,sizeof(qreal));
aItem=theModel->item(i,3); //位移
qreal weiYi=aItem->data(Qt::DisplayRole).toFloat();
aStream.writeRawData((char *)&weiYi,sizeof(qreal));
aItem=theModel->item(i,4); //固井质量
QString zhiLiang=aItem->data(Qt::DisplayRole).toString();
btArray=zhiLiang.toUtf8();
aStream.writeBytes(btArray,btArray.length()); //写入长度,uint,然后是字符串
aItem=theModel->item(i,5); //测井取样
bool quYang=(aItem->checkState()==Qt::Checked); //true or false
if (quYang)
aStream.writeRawData((char *)&yes,sizeof(qint8));
else
aStream.writeRawData((char *)&no,sizeof(qint8));
}
aFile.close();
return true;
}
//从标准编码文件dat中读入
QString curPath=QDir::currentPath();//系统当前目录
QString aFileName=QFileDialog::getOpenFileName(this,tr("打开一个文件"),curPath,"二进制数据文件(*.dat)");
if (aFileName.isEmpty())
return;
if (openBinaryFile(aFileName)) //保存为流数据文件
QMessageBox::information(this,"提示消息","文件已经打开!");
bool MainWindow::openBinaryFile(QString &aFileName)
{
QFile aFile(aFileName);
if (!(aFile.open(QIODevice::ReadOnly)))
return false;
QDataStream aStream(&aFile); //用文本流读取文件
aStream.setByteOrder(QDataStream::LittleEndian);//保存的时候是小端
qint16 rowCount,colCount;
aStream.readRawData((char *)&rowCount, sizeof(qint16));
aStream.readRawData((char *)&colCount, sizeof(qint16));
this->resetTable(rowCount);
//获取表头文字,但是并不利用
char *buf;
uint strLen; //也就是 quint32
for (int i = 0;i < colCount;i++)
{
aStream.readBytes(buf,strLen);//同时读取字符串长度,和字符串内容
QString str=QString::fromLocal8Bit(buf,strLen); //可处理汉字
}
//获取数据区数据
QStandardItem *aItem;
qint16 ceShen;
qreal chuiShen;
qreal fangWei;
qreal weiYi;
QString zhiLiang;
qint8 quYang; //分别代表逻辑值 true和false
QModelIndex index;
for (int i=0;i<rowCount;i++)
{
aStream.readRawData((char *)&ceShen, sizeof(qint16)); //测深
index=theModel->index(i,0);
aItem=theModel->itemFromIndex(index);
aItem->setData(ceShen,Qt::DisplayRole);
aStream.readRawData((char *)&chuiShen, sizeof(qreal)); //垂深
index=theModel->index(i,1);
aItem=theModel->itemFromIndex(index);
aItem->setData(chuiShen,Qt::DisplayRole);
aStream.readRawData((char *)&fangWei, sizeof(qreal)); //方位
index=theModel->index(i,2);
aItem=theModel->itemFromIndex(index);
aItem->setData(fangWei,Qt::DisplayRole);
aStream.readRawData((char *)&weiYi, sizeof(qreal)); //位移
index=theModel->index(i,3);
aItem=theModel->itemFromIndex(index);
aItem->setData(weiYi,Qt::DisplayRole);
aStream.readBytes(buf,strLen);//固井质量
zhiLiang=QString::fromLocal8Bit(buf,strLen);
index=theModel->index(i,4);
aItem=theModel->itemFromIndex(index);
aItem->setData(zhiLiang,Qt::DisplayRole);
aStream.readRawData((char *)&quYang, sizeof(qint8)); //测井取样
index=theModel->index(i,5);
aItem=theModel->itemFromIndex(index);
if (quYang==1)
aItem->setCheckState(Qt::Checked);
else
aItem->setCheckState(Qt::Unchecked);
}
aFile.close();
return true;
}