作者:zzssdd2

E-mail:zzssdd2@foxmail.com

一、前言

开发环境:Qt5.12.10 + MinGW

功能

  • 文件的发送
  • 数据的保存

知识点

  • QFile类的使用
  • QTimer类的使用
  • 文本的转码与编码识别
  • QPushButtonQProgressBar控件的使用

二、功能实现

本章功能主要包含两个方面,一是通过串口发送选定的文本文件,二是将接收的数据保存为本地文本文件。最后还有对《QT串口助手(三):数据接收》章节内容进行一个补充扩展。

2.1、文件打开

选择文件按钮点击后,触发点击信号对应的槽函数,在槽函数中进行文件的打开与读取:

  1. /*选择并打开文件*/
  2. QString curPath = QDir::currentPath(); //系统当前目录
  3. QString dlgTitle = "打开文件"; //对话框标题
  4. QString filter = "文本文件(*.txt);;二进制文件(*.bin *.dat);;所有文件(*.*)"; //文件过滤器
  5. QString filepath = QFileDialog::getOpenFileName(this,dlgTitle,curPath,filter);
  6. QFileInfo fileinfo(filepath);
  7. if (filepath.isEmpty())
  8. {
  9. QMessageBox::warning(this,"警告","文件为空");
  10. return;
  11. }
  12. //文件路径显示到发送框
  13. ui->Send_TextEdit->clear();
  14. ui->Send_TextEdit->appendPlainText(filepath);
  15. QFile file(filepath);
  16. if (!file.exists())
  17. {
  18. QMessageBox::warning(this,"警告","文件不存在");
  19. return;
  20. }
  21. if (!file.open(QIODevice::ReadOnly))
  22. {
  23. QMessageBox::warning(this,"警告","文件打开失败");
  24. return;
  25. }

2.2、编码判断

因为应用程序默认使用的编码为UTF-8,如果打开GBK格式编码的文件就会乱码,所以需要判断文件的编码,如果不是UTF-8则需要对文件进行编码转换。

  1. /* 设置应用程序的编码解码器 */
  2. QTextCodec *codec = QTextCodec::codecForName("UTF-8");
  3. QTextCodec::setCodecForLocale(codec);
  • [static] QTextCodec *QTextCodec::codecForName(const char *name)

    Searches all installed QTextCodec objects and returns the one which best matches name; the match is case-insensitive. Returns 0 if no codec matching the name name could be found.

  • [static] void QTextCodec::setCodecForLocale(QTextCodec **c*)

    Set the codec to c; this will be returned by codecForLocale(). If c is a null pointer, the codec is reset to the default.

    This might be needed for some applications that want to use their own mechanism for setting the locale.

    Warning: This function is not reentrant.

  1. /* 判断编码 */
  2. QTextCodec::ConverterState state;
  3. QTextCodec *codec = QTextCodec::codecForName("UTF-8");
  4. FileText = codec->toUnicode(data.constData(),data.size(),&state);
  5. //若有无效字符则是GBK编码
  6. if (state.invalidChars > 0)
  7. {
  8. //转码后返回
  9. FileText = QTextCodec::codecForName("GBK")->toUnicode(data);
  10. }
  11. else
  12. {
  13. FileText = data;
  14. }

对文件进行上述的处理后,如果是GBK编码则也能够正确的读取了。

  • QString QTextCodec::toUnicode(const char *input, int size, QTextCodec::ConverterState *state = nullptr) const

    Converts the first size characters from the input from the encoding of this codec to Unicode, and returns the result in a QString.

    The state of the convertor used is updated.

  • QString QTextCodec::toUnicode(const QByteArray &a) const

    Converts a from the encoding of this codec to Unicode, and returns the result in a QString.

2.3、文件读取

文件打开后,需要对文件类型进行判断,然后进行文件数据的读取:

  1. /*判断文件类型*/
  2. int type = 0;
  3. QMimeDatabase db;
  4. QMimeType mime = db.mimeTypeForFile(filepath);
  5. if (mime.name().startsWith("text/"))
  6. {
  7. type = 1; //文本文件
  8. }
  9. else if (mime.name().startsWith("application/"))
  10. {
  11. type = 2; //二进制文件
  12. }

QMimeType QMimeDatabase::mimeTypeForFile(const QString&fileName, QMimeDatabase::MatchMode mode = MatchDefault) const

Returns a MIME type for the file named fileName using mode.

This is an overloaded function.

QMimeType 类描述文件或数据的类型,由 MIME 类型字符串表示,获取到文件类型后接下来就知道应该使用什么方法读取文件内容了。常见文件类型如下:

描述(startsWith) 类型 示例
text 普通文本 text/plain, text/html, text/css, text/javascript
image 图像文件(包含动态gif) image/gif, image/png, image/jpeg, image/bmp, image/webp
audio 音频文件 audio/wav, audio/mpeg, audio/midi, audio/webm, audio/ogg
video 视频文件 video/mp4, video/x-flv, video/webm, video/ogg
application 二进制数据 application/xml, application/pdf
  1. /*读取文件*/
  2. switch(type)
  3. {
  4. case 1:
  5. {
  6. //QIODevice读取普通文本
  7. QByteArray data = file.readAll();
  8. file.close();
  9. if (data.isEmpty())
  10. {
  11. QMessageBox::information(this, "提示", "文件内容空");
  12. return;
  13. }
  14. /* 判断编码 */
  15. }
  16. break;
  17. case 2:
  18. {
  19. int filelen = fileinfo.size();
  20. QVector<char> cBuf(filelen);//储存读出数据
  21. //使用QDataStream读取二进制文件
  22. QDataStream datain(&file);
  23. datain.readRawData(&cBuf[0],filelen);
  24. file.close();
  25. //char数组转QString
  26. FileText = QString::fromLocal8Bit(&cBuf[0],filelen);
  27. }
  28. break;
  29. }
  • QByteArray QIODevice::readAll()

    Reads all remaining data from the device, and returns it as a byte array.

    This function has no way of reporting errors; returning an empty QByteArray can mean either that no data was currently available for reading, or that an error occurred.

  • int QDataStream::readRawData(char **s*, int len)

    Reads at most len bytes from the stream into s and returns the number of bytes read. If an error occurs, this function returns -1.

    The buffer s must be preallocated. The data is not decoded.

  • 关于QVector:QVector类是一个提供动态数组的模板类。QVector是Qt的通用容器类之一,它将其项存储在相邻的内存位置并提供基于索引的快速访问。例如上面代码定义了一个大小为filelen的char类型的数组用来储存读取的二进制数据(相对与普通数组而言,QVector数组项可以动态增加,能够避免访问越界等问题)。

  • QT中使用QTextStream或QIODevice类读写普通文本文件,使用QDataStream类读写二进制文本文件

最后将读取到的文件大小信息和内容显示到接收框并标记有待发送文件:

  1. //显示文件大小信息
  2. QString info = QString("%1%2").arg("文件大小为:").arg(FileText.length());
  3. ui->Receive_TextEdit->clear();
  4. ui->Receive_TextEdit->append(info);
  5. //显示文件内容
  6. if (ui->HexDisp_checkBox->isChecked())
  7. {
  8. ui->Receive_TextEdit->setPlainText(FileText.toUtf8().toHex(' ').toUpper());
  9. }
  10. else
  11. {
  12. ui->Receive_TextEdit->setPlainText(FileText);
  13. }
  14. //设置显示焦点在最顶部
  15. ui->Receive_TextEdit->moveCursor(QTextCursor::Start,QTextCursor::MoveAnchor);
  16. /*标记有文件发送*/
  17. isSendFile = true;
  18. FrameCount = 0;
  19. ProgressBarValue = 0;

2.4、文件发送

此时在标记了有文件发送的情况下,点击发送按钮则是发送文件:

  1. /*
  2. 函 数:on_Send_Bt_clicked
  3. 描 述:发送按键点击信号槽
  4. 输 入:无
  5. 输 出:无
  6. */
  7. void Widget::on_Send_Bt_clicked()
  8. {
  9. if (isSerialOpen != false)
  10. {
  11. if (isSendFile) //发送文件数据
  12. {
  13. if (ui->Send_Bt->text() == "发送")
  14. {
  15. ui->Send_Bt->setText("停止");
  16. SendFile();
  17. }
  18. else
  19. {
  20. ui->Send_Bt->setText("发送");
  21. FileSendTimer->stop();
  22. }
  23. }
  24. else //发送发送框数据
  25. {
  26. SerialSendData(SendTextEditBa);
  27. }
  28. }
  29. else
  30. {
  31. QMessageBox::information(this, "提示", "串口未打开");
  32. }
  33. }
  34. /*
  35. 函 数:SendData
  36. 描 述:定时器发送文件
  37. 输 入:无
  38. 输 出:无
  39. */
  40. void Widget::SendFile(void)
  41. {
  42. /*按设置参数发送*/
  43. FrameLen = ui->PackLen_lineEdit->text().toInt(); // 帧大小
  44. FrameGap = ui->GapTim_lineEdit->text().toInt(); // 帧间隔
  45. int textlen = Widget::FileText.size(); // 文件大小
  46. if (FrameGap <= 0 || textlen <= FrameLen)
  47. {
  48. //时间间隔为0 或 帧大小≥文件大小 则直接一次发送
  49. serial->write(FileText.toUtf8());
  50. ui->Send_Bt->setText("发送");
  51. }
  52. else
  53. {
  54. //按设定时间和长度发送
  55. FrameNumber = textlen / FrameLen; // 包数量
  56. LastFrameLen = textlen % FrameLen; // 最后一包数据长度
  57. //进度条步进值
  58. if (FrameNumber >= 100)
  59. {
  60. ProgressBarStep = FrameNumber / 100;
  61. }
  62. else
  63. {
  64. ProgressBarStep = 100 / FrameNumber;
  65. }
  66. //设置定时器
  67. FileSendTimer->start(FrameGap);
  68. }
  69. }

设置一个定时器,将文件按照设定的帧大小帧间隔来发送:

  1. /*文件帧发送定时器信号槽*/
  2. FileSendTimer = new QTimer(this);
  3. connect(FileSendTimer,SIGNAL(timeout()),this,SLOT(File_TimerSend()));
  4. /*
  5. 函 数:File_TimerSend
  6. 描 述:发送文件定时器槽函数
  7. 输 入:无
  8. 输 出:无
  9. */
  10. void Widget::File_TimerSend(void)
  11. {
  12. if (FrameCount < FrameNumber)
  13. {
  14. serial->write(FileText.mid(FrameCount * FrameLen, FrameLen).toUtf8());
  15. FrameCount++;
  16. //更新进度条
  17. ui->progressBar->setValue(ProgressBarValue += ProgressBarStep);
  18. }
  19. else
  20. {
  21. if (LastFrameLen > 0)
  22. {
  23. serial->write(FileText.mid(FrameCount * FrameLen, LastFrameLen).toUtf8());
  24. ui->progressBar->setValue(100);
  25. }
  26. /*发送完毕*/
  27. FileSendTimer->stop();
  28. FrameCount = 0;
  29. ProgressBarValue = 0;
  30. FrameNumber = 0;
  31. LastFrameLen = 0;
  32. QMessageBox::information(this, "提示", "发送完成");
  33. ui->progressBar->setValue(0);
  34. ui->Send_Bt->setText("发送");
  35. }
  36. }

QString QString::mid(int position, int n = -1) const

Returns a string that contains n characters of this string, starting at the specified position index.

Returns a null string if the position index exceeds the length of the string. If there are less than n characters available in the string starting at the given position, or if n is -1 (default), the function returns all characters that are available from the specified position.

Example:

  1. QString x = "Nine pineapples";
  2. QString y = x.mid(5, 4); // y == "pine"
  3. QString z = x.mid(5); // z == "pineapples"

2.5、数据保存

保存数据按钮按下时,将接收框数据按文本方式进行保存:

  1. /*
  2. 函 数:on_SaveData_Button_clicked
  3. 描 述:保存数据按钮点击槽函数
  4. 输 入:无
  5. 输 出:无
  6. */
  7. void Widget::on_SaveData_Button_clicked()
  8. {
  9. QString data = ui->Receive_TextEdit->toPlainText();
  10. if (data.isEmpty())
  11. {
  12. QMessageBox::information(this, "提示", "数据内容空");
  13. return;
  14. }
  15. QString curPath = QDir::currentPath(); //获取系统当前目录
  16. QString dlgTitle = "保存文件"; //对话框标题
  17. QString filter = "文本文件(*.txt);;所有文件(*.*)"; //文件过滤器
  18. QString filename = QFileDialog::getSaveFileName(this,dlgTitle,curPath,filter);
  19. if (filename.isEmpty())
  20. {
  21. return;
  22. }
  23. QFile file(filename);
  24. if (!file.open(QIODevice::WriteOnly))
  25. {
  26. return;
  27. }
  28. /*保存文件*/
  29. QTextStream stream(&file);
  30. stream << data;
  31. file.close();
  32. }

QTextStream::QTextStream(FILE **fileHandle, QIODevice::OpenModeopenMode* = QIODevice::ReadWrite)

Constructs a QTextStream that operates on fileHandle, using openMode to define the open mode. Internally, a QFile is created to handle the FILE pointer.

This constructor is useful for working directly with the common FILE based input and output streams: stdin, stdout and stderr. Example:

  1. QString str;
  2. QTextStream in(stdin);
  3. in >> str;

三、扩展

这里对接收模块的功能进行一个补充。上面说了应用程序默认编码是UTF-8,那么如果在ascii显示模式下收到的数据是GBK格式的编码就会造成显示乱码,所以需要对接收数据进行编码判断,如果是GBK编码则进行转换显示。

  1. /*
  2. 函 数:SerialPortReadyRead_slot
  3. 描 述:readyRead()信号对应的数据接收槽函数
  4. 输 入:无
  5. 输 出:无
  6. */
  7. void Widget::SerialPortReadyRead_slot()
  8. {
  9. /*读取串口收到的数据*/
  10. QByteArray bytedata = serial->readAll();
  11. //......省略
  12. if(ui->HexDisp_checkBox->isChecked() == false) //ascii显示
  13. {
  14. /* 判断编码 */
  15. QTextCodec::ConverterState state;
  16. //调用utf8转unicode的方式转码,获取转换结果
  17. QString asciidata = QTextCodec::codecForName("UTF-8")->toUnicode(bytedata.constData(),bytedata.size(),&state);
  18. //存在无效字符则是GBK,转码后返回
  19. if (state.invalidChars > 0)
  20. {
  21. asciidata = QTextCodec::codecForName("GBK")->toUnicode(bytedata);
  22. }
  23. else
  24. {
  25. asciidata = QString(bytedata);
  26. }
  27. //......省略
  28. }
  29. //......省略
  30. }

四、总结

本章主要讲解对文件的操作。除了需要了解在QT中对文件类的常用操作之外,个人认为还有比较重要的两个知识点:1是文本编码的判断和转码处理,2是对于二进制文本的读取。

QT串口助手(五):文件操作的更多相关文章

  1. QT串口助手(四):数据发送

    作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.前言 开发环境:Qt5.12.10 + MinGW 实现的功能 串口数据的发送 ascii字符与hex字符的相互转换 自 ...

  2. paip.c++ qt 目录遍历以及文件操作

    paip.c++ qt 目录遍历以及文件操作 作者Attilax ,  EMAIL:1466519819@qq.com  来源:attilax的专栏 地址:http://blog.csdn.net/a ...

  3. QT串口助手(三):数据接收

    作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.前言 开发环境:Qt5.12.10 + MinGW 实现的功能 串口数据的接收 ascii字符形式显示与hex字符形式显 ...

  4. Qt之课外实践——文件操作(简单清道夫)

    说明:这个小项目是关于文件操作的.主要的功能有:重复文件的查找(根据文件的大小),说白了,就是讲大小相同的文件在一起显示出来,供用户自由的选择删除.这个360云盘里的文件去重还差的很远.还有空文件夹的 ...

  5. python之路(五)-文件操作

    文件操作无非两个,即:读.写 python 2.x: 文件句柄 = file('文件路径', '模式') python3.x: 文件句柄 = open('文件路径', '模式') 打开文件的模式有: ...

  6. python成长之路五-文件操作

    1,文件操作 f = open("D:\种子.txt",encoding="utf-8",mode="r") # 打开一个种子.txt文件, ...

  7. python高级 之(五) --- 文件操作

    文件操作 """ 在程序中操作的文件内容: 1. 读取文件中的内容 2. 向文件中写入内容 首先: 在程序中与文件建立一个通道,通过通道操作文件指针,达到所要的结果 向文 ...

  8. 4.关于QT中的QFile文件操作,QBuffer,Label上添加QPixmap,QByteArray和QString之间的区别,QTextStream和QDataStream的区别,QT内存映射(

     新建项目13IO 13IO.pro HEADERS += \ MyWidget.h SOURCES += \ MyWidget.cpp QT += gui widgets network CON ...

  9. 4.关于QT中的QFile文件操作,QBuffer,Label上加入QPixmap,QByteArray和QString之间的差别,QTextStream和QDataStream的差别,QT内存映射(

     新建项目13IO 13IO.pro HEADERS += \ MyWidget.h SOURCES += \ MyWidget.cpp QT += gui widgets network CON ...

随机推荐

  1. MyBatis初级实战之三:springboot集成druid

    OpenWrite版: 欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kuber ...

  2. Java中的基本数据类型与引用数据类型

    一.基本数据类型 byte.short.int.long(整数类型) float.double(浮点数类型) char(字符型) boolean(布尔类型 ) Java数据大多数存放在堆栈中. 栈区: ...

  3. RocketMQ—消息队列入门

    消息队列功能介绍 字面上说的消息队列是数据结构中"先进先出"的一种数据结构,但是如果要求消除单点故障,保证消息传输可靠性,应对大流量的冲击,对消息队列的要求就很高了.现在互联网的& ...

  4. 《进击吧!Blazor!》第一章 2.Hello Blazor

    第二次写专栏,开头还是不知道说什么,所以--先来段广告<进击吧!Blazor!>是本人与张善友老师合作的Blazor零基础入门系列视频,此系列能让一个从未接触过Blazor的程序员掌握开发 ...

  5. 记一次 RocketMQ broker 因内存不足导致的启动失败

    原创:西狩 编写日期 / 修订日期:2020-01-12 / 2020-01-12 版权声明:本文为博主原创文章,遵循 CC BY-SA-4.0 版权协议,转载请附上原文出处链接和本声明. 背景 该小 ...

  6. 三十二:WEB漏洞-文件操作之文件下载读取全解

    文件下载读取 原路,检测,利用,修复 利用 数据库配置文件下载或者读取后续 接口密钥信息文件下载或者读取后续 文件名,参数值,目录符号 read.xxx?filename= down.xxx?file ...

  7. Django--虛擬環境Virtualenv的安裝使用

    Django--虛擬環境Virtualenv的安裝使用 本次隨筆只要記錄在windows下安裝virtualenvwrapper,以及簡單的使用命令. virtualenvwrapper的安裝     ...

  8. code-server scala error: object apache is not a member of package org

    原因是scala缺少包,需要把spark或对应的包放入scala目录下的lib,然后重启主机,在terminal输入reboot即可. 如果不重启主机,则在交互式编程中可以成功import.但是直接在 ...

  9. Linux下unix socket 读写 抓包

    Linux下unix socket 读写 抓包-ubuntuer-ChinaUnix博客 http://blog.chinaunix.net/uid-9950859-id-247877.html

  10. Covering Indexes in MySQL, PostgreSQL, and MongoDB

    Covering Indexes in MySQL, PostgreSQL, and MongoDB - Orange Matter https://orangematter.solarwinds.c ...