工作变动开始接触qt,涉及到qt读文件相关业务,简单整理一下qt读文件几种方式,后面发现有其他的再新增
1:使用qt读文件显示在界面的时候发现特别慢(实际是界面显示过多/日志打印导致)
测试demo的方式归纳汇总几种读qt文件的方法:
1:获取文件源按钮,并打印获取到的相关文件名
2:使用qfile直接读文件,显示耗时时间
3:使用qdatastream读文件,显示耗时时间
4:使用qtextstrean读文件,显示耗时时间
5:使用内存映射 Qfile::map 内存映射读文件,这里的map实际是Qfile的基类中的
2:qflie直接读文件
按行读取,read按字节读取,readAll读全部几种方式
void frmReadFile::on_qfileReadFileButton_clicked() { //使用qfile readline /readAll QFile file_read(m_filename); if(!file_read.exists()) { return; } if (!file_read.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, "警告", "文件打不开!"); return; } //按行读取的方式 // QByteArray readdata_array; // while(!file_read.atEnd()) // { // readdata_array = file_read.readLine(); // //真正的耗时在显示在控件上 如果增加显示在控件上 可能需要耗时22s // ui->printTextEdit->append(readdata_array.toHex(' ')); // qDebug()<<"read file size:"<<readdata_array.size(); // readdata_array.clear(); // } //read读取特定字节 int readlength = 0; QByteArray byte_data; byte_data.resize(1000); while((readlength=file_read.read(byte_data.data(),1000))!=0) // 一次读进1000个字节(读进1000个字节,length==1000),当剩余字节数小于1000时,length等于剩余字节数 { this->append(QString(byte_data.toHex(' '))); } //readall直接读取全部内容 // QByteArray readdata_array; // file_read.seek(0); // readdata_array= file_read.readAll(); // ui->printTextEdit->append(readdata_array.toHex(' ')); // qDebug()<<"read file size:"<<readdata_array.size() <<"data is:"<<readdata_array.toHex(' '); // readdata_array.clear(); file_read.close(); }
3:qTextStream读文件
借助readLine readAll read 可以实现文件读取
通过用QElapsedTimer增加耗时,分析出文件处理真正耗时在内容显示上。
写时发现待处理问题:打开文件的方式,以及读取显示中文的demo。
void frmReadFile::on_qTextStrReadFileButton_clicked() { QFile file_read(m_filename); if(!file_read.exists()) { return; } if (!file_read.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, "警告", "文件打不开!"); return; } //读取并显示在控件上 耗时7s QElapsedTimer lost_time; lost_time.start(); QString read_data= ""; QTextStream out(&file_read); while(!out.atEnd()) { read_data += out.readLine(); // ui->printTextEdit->append(read_data); //耗时7s } QString qfile_lost_time = "qtextstream 读取文件耗时:"; qfile_lost_time += QString::number(lost_time.elapsed()); qfile_lost_time += "毫秒"; ui->TestTextEdit->append(qfile_lost_time); //先读取再显示的方案 读取耗时59ms 主要显示也都是乱码 //这里如果转成16进制进行输出 内存占用太大太大 会导致程序卡死 // ui->printTextEdit->append(read_data.toLatin1().toHex()); ui->printTextEdit->append(read_data); file_read.close(); }
4:qdatastream读文件
可以通过readBytes, readRawData实现文件的读取
写时发现待处理问题:遇到过open文件时选择打开方式 如果以Text方式打开,流显示换行符号会是两个字节,所以在处理读文件时,open函数的参数要注意
//使用qdatastream进行读文件 //耗时挺短的 读文件并不怎么耗时 耗时在处理显示上 void frmReadFile::on_qdataStrReadFileButton_clicked() { QFile file_read(m_filename); if(!file_read.exists()) { return; } if (!file_read.open(QIODevice::ReadOnly)){ QMessageBox::warning(this, "警告", "文件打不开!"); return; } QElapsedTimer lost_time; lost_time.start(); QDataStream out(&file_read); QByteArray read_data; read_data.resize(file_read.size()); //一次性读取内容过多 导致文件读不出来 while(!out.atEnd()) { out.readRawData(read_data.data(), file_read.size()); } QString qfile_lost_time = "qdatastream 读取文件耗时:"; qfile_lost_time += QString::number(lost_time.elapsed()); qfile_lost_time += "毫秒"; ui->TestTextEdit->document()->setMaximumBlockCount(100); ui->TestTextEdit->append(qfile_lost_time); qDebug()<<"read file size:"<<read_data.size(); //这里进行显示的时候如果过大总会崩溃 ===》内容过大到界面显示的问题 ui->printTextEdit->document()->setMaximumBlockCount(100); ui->printTextEdit->append(read_data.toHex(' ')); lost_time.restart(); //TODO 1:研究一下通过信号触发容器的写入 2:研究一下QByteArray转成带空格的耗时 file_read.close(); }
5:内存映射的方式读文件
其实就是把指针映射到文件的对应位置,然后相关的操作就和操作内存指针一样了。
主要用到了 QFileDevice 类的 map 和unmap
//内存映射的方法读文件 void frmReadFile::on_qmapReadFileButton_clicked() { //采用内存映射的方法进行 QFile file_read(m_filename); if (!file_read.open(QIODevice::ReadOnly)) { QMessageBox::warning(this, "警告", "文件打不开!"); return; } QElapsedTimer lost_time; lost_time.start(); int file_size = file_read.size(); uchar* ptr = file_read.map(0, file_read.size()); uchar* flag_ptr = ptr; //这里先关闭文件对读文件不影响 int temp = 0; QByteArray flag_data; flag_data.resize(1000); while(temp < file_size) { memcpy(flag_data.data(), flag_ptr+temp, 1000); temp += 1000; //这里对数据进行处理 //TODO 这里的打印会导致qt工具打印过多日志 其他工具崩溃 // qDebug()<<"file map data0 is"<<flag_data; // ui->printTextEdit->append(flag_data.toHex()); //这里如果读取文件过大 也会让程序占用的内存大 this->append(QString(flag_data.toHex(' '))); } if(temp>file_size) { memcpy(flag_data.data(), flag_ptr+(file_size-file_size%1000), file_size%1000); qDebug()<<"file map data1 is"<<flag_data; ui->printTextEdit->append(flag_data.toHex(' ')); // this->append(QString(flag_data.toHex(' '))); } QString qfile_lost_time = "qfile map 读取文件耗时:"; qfile_lost_time += QString::number(lost_time.elapsed()); qfile_lost_time += "毫秒"; qfile_lost_time += QString::number(file_size/1000); qfile_lost_time += "行"; ui->TestTextEdit->append(qfile_lost_time); file_read.unmap(ptr); if(ptr!=NULL) { ptr = NULL; qDebug()<<"umap is not null"; } file_read.close(); }
6:总结
这些demo测试的初衷是,初次接触qt,要处理一批大文件后,进行界面的显示,但是发现,读取大文件时,有崩溃,耗时的问题。
排查耗时崩溃问题的原因,有怀疑过是读文件的耗时,所以做了简单demo测试,最后发现是字符处理内存增加,qDebug日志记录,通过ui进行界面显示过大内容导致的耗时,甚至崩溃。
在整理回头整理demo的过程,以及最近实际过程中遇到的小问题,发现可以整理的点:
1:open的时候,打开方式按默认,文本方式,流的方式打开,对后面文件处理有影响(例如,以文本方式打开,但是用qdatastream处理流的时候发现,换行符变成了两个字符)。
2:几种方式排查后,读文件并不耗时,可以整理几种读文件方式的原理。
3:研究几种读文件后,字符处理中文显示问题。