当前位置: 首页 > news >正文

开源 C++ QT QML 开发(九)文件--文本和二进制

           文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

开源 C++ QT QML 开发(八)自定义控件--圆环

开源 C++ QT QML 开发(九)文件--文本和二进制

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:实现了一个简单文件编辑器应用程序,可以对文本文件和二进制文件打开和保存。

1.代码分析

2.所有源码

3.效果演示

一、代码分析

1. FileHandler 头文件分析
1.1 类声明和属性定义
 

class FileHandler : public QObject
{Q_OBJECTQ_PROPERTY(QString fileName READ fileName NOTIFY fileNameChanged)Q_PROPERTY(bool isBinary READ isBinary NOTIFY isBinaryChanged)Q_PROPERTY(bool fileModified READ fileModified NOTIFY fileModifiedChanged)Q_PROPERTY(QVariantList hexData READ hexData NOTIFY hexDataChanged)Q_PROPERTY(QString fileContent READ fileContent NOTIFY fileContentChanged)


分析:

使用 Qt 属性系统,所有属性都是只读的(只有 READ,没有 WRITE)

NOTIFY 信号确保属性变化时 QML 能自动更新

QVariantList 用于在 C++ 和 QML 之间传递复杂数据

1.2 信号定义
 

signals:void fileNameChanged();void isBinaryChanged();void fileModifiedChanged();void hexDataChanged();void fileContentChanged();void fileOpened(const QString &fileName, bool isBinary);void fileSaved(const QString &fileName);void fileClosed();void errorOccurred(const QString &errorMessage);


分析:

前5个信号对应属性变化通知

后4个信号是业务逻辑事件通知

fileOpened 和 errorOccurred 携带参数传递详细信息

2. FileHandler 实现文件分析
2.1 构造函数
 

FileHandler::FileHandler(QObject *parent): QObject(parent), m_isBinary(false), m_fileModified(false)
{
}


分析:

初始化成员变量,默认文件类型为文本,未修改状态

2.2 openFile 函数
 

bool FileHandler::openFile(const QString &filePath)
{QFile file(filePath);if (!file.exists()) {emit errorOccurred(tr("文件不存在: %1").arg(filePath));return false;}


文件存在性检查:

使用 QFile::exists() 检查文件是否存在

如果不存在,发射错误信号并返回 false


   

 if (!file.open(QIODevice::ReadOnly)) {emit errorOccurred(tr("无法打开文件: %1").arg(filePath));return false;}


文件打开检查:

尝试以只读模式打开文件

如果打开失败(权限问题、文件被占用等),发射错误信号

    m_fileName = filePath;m_isBinary = isBinaryFile(filePath);if (m_isBinary) {// 读取二进制文件QByteArray data = file.readAll();m_originalData = data;m_fileContent.clear();processBinaryData(data);} else {// 读取文本文件QTextStream stream(&file);stream.setCodec("UTF-8");m_fileContent = stream.readAll();m_originalData = m_fileContent.toUtf8();// 清空二进制数据m_hexData.clear();emit hexDataChanged();emit fileContentChanged();emit fileOpened(filePath, false);}


文件内容处理:

二进制文件:直接读取原始数据,调用 processBinaryData 处理

文本文件:使用 QTextStream 以 UTF-8 编码读取,保存内容和原始数据

清理不相关的数据并发射相应信号
   

file.close();m_fileModified = false;emit fileNameChanged();emit isBinaryChanged();emit fileModifiedChanged();return true;
}


收尾工作:

关闭文件,重置修改状态

发射属性变化信号通知界面更新


2.3 processBinaryData 函数
 

void FileHandler::processBinaryData(const QByteArray &data)
{m_hexData.clear();// 预分配空间,提高性能int lineCount = (data.size() + 15) / 16; // 计算总行数m_hexData.reserve(lineCount);const uchar *bytes = reinterpret_cast<const uchar*>(data.constData());int dataSize = data.size();


性能优化:

预计算行数:每行16字节,计算需要的行数

预分配 QVariantList 容量避免重复分配

使用原始指针访问提高效率

    for (int i = 0; i < dataSize; i += 16) {QString hexLine;hexLine.reserve(48); // 预分配空间:16字节 * 3字符for (int j = 0; j < 16 && i + j < dataSize; j++) {uchar byte = bytes[i + j];QString hexByte = QString("%1 ").arg(byte, 2, 16, QChar('0')).toUpper();hexLine.append(hexByte);}// 移除最后一个空格if (!hexLine.isEmpty() && hexLine.endsWith(' ')) {hexLine.chop(1);}


十六进制格式化:

每行处理最多16个字节

使用 QString::arg() 格式化为2位十六进制,不足补零

移除行末多余的空格

QVariantMap lineData;lineData["address"] = QString("%1").arg(i, 8, 16, QChar('0')).toUpper();lineData["hex"] = hexLine;m_hexData.append(lineData);}emit hexDataChanged();emit fileOpened(m_fileName, true);
}


数据结构构建:

使用 QVariantMap 存储每行的地址和十六进制数据

地址格式化为8位十六进制数

发射信号通知数据就绪2.4 saveFile 函数
 

bool FileHandler::saveFile(const QString &content)
{if (m_fileName.isEmpty()) {emit errorOccurred(tr("没有指定文件名"));return false;}QFile file(m_fileName);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {emit errorOccurred(tr("无法保存文件: %1").arg(m_fileName));return false;}QTextStream stream(&file);stream.setCodec("UTF-8");stream << content;file.close();


文本文件保存:

检查文件名有效性

以文本模式写入,使用 UTF-8 编码

使用 QTextStream 确保编码正确

m_fileContent = content;m_originalData = content.toUtf8();m_fileModified = false;emit fileContentChanged();emit fileSaved(m_fileName);emit fileModifiedChanged();return true;
}


状态更新:

更新内存中的内容和原始数据

重置修改标志

发射保存成功信号2.5 saveBinaryFile 函数
 

bool FileHandler::saveBinaryFile(const QVariantList &hexData)
{// ... 文件名和文件打开检查类似 saveFile ...// 将十六进制数据转换回二进制QByteArray binaryData;for (const QVariant &line : hexData) {QString hexLine = line.toString();QByteArray lineData = hexStringToByteArray(hexLine);binaryData.append(lineData);}file.write(binaryData);file.close();


二进制数据重建:

遍历十六进制数据列表

调用 hexStringToByteArray 转换每行数据

直接写入二进制数据2.6 hexStringToByteArray 函数
 

QByteArray FileHandler::hexStringToByteArray(const QString &hexString)
{QByteArray byteArray;QStringList hexBytes = hexString.split(' ', Qt::SkipEmptyParts);byteArray.reserve(hexBytes.size());for (const QString &hexByte : hexBytes) {bool ok;char byte = static_cast<char>(hexByte.toInt(&ok, 16));if (ok) {byteArray.append(byte);}}return byteArray;
}


十六进制解析:

按空格分割十六进制字符串

使用 toInt(&ok, 16) 将十六进制字符串转换为整数

只添加成功转换的字节2.7 isBinaryFile 函数
 

bool FileHandler::isBinaryFile(const QString &filePath)
{QFileInfo fileInfo(filePath);QString extension = fileInfo.suffix().toLower();static const QStringList binaryExtensions = {"exe", "dll", "bin", "dat", "img", "so", "dylib","jpg", "jpeg", "png", "gif", "bmp", "ico","pdf", "doc", "docx", "xls", "xlsx","zip", "rar", "7z", "tar", "gz"};return binaryExtensions.contains(extension);
}


文件类型判断:

基于文件扩展名的简单判断

使用 static const 避免重复构造列表

包含常见二进制文件格式

3. QML 界面分析
3.1 二进制显示更新函数
 

function updateBinaryDisplay() {var displayText = ""for (var i = 0; i < fileHandler.hexData.length; i++) {var line = fileHandler.hexData[i]displayText += line.address + "    " + line.hex + "\n"}textArea.text = displayText
}


显示格式化:

遍历十六进制数据,构建显示字符串

格式:地址 十六进制数据

每行以换行符结束3.2 保存二进制文件逻辑
 

var lines = textArea.text.split('\n')
var hexData = []for (var i = 0; i < lines.length; i++) {if (lines[i].trim() === "") continuevar lineParts = lines[i].split('    ')if (lineParts.length >= 2) {hexData.push(lineParts[1].trim())}
}

fileHandler.saveBinaryFile(hexData)
数据提取:

从显示的文本中解析出十六进制数据

跳过空行,按分隔符分割地址和十六进制数据

只提取十六进制部分传递给 C++

二、所有源码

FileHandler.h文件源码

#ifndef FILEHANDLER_H
#define FILEHANDLER_H#include <QObject>
#include <QString>
#include <QByteArray>
#include <QFile>
#include <QTextStream>
#include <QDataStream>
#include <QVector>
#include <QVariantList>
#include <QFileInfo>class FileHandler : public QObject
{Q_OBJECTQ_PROPERTY(QString fileName READ fileName NOTIFY fileNameChanged)Q_PROPERTY(bool isBinary READ isBinary NOTIFY isBinaryChanged)Q_PROPERTY(bool fileModified READ fileModified NOTIFY fileModifiedChanged)Q_PROPERTY(QVariantList hexData READ hexData NOTIFY hexDataChanged)Q_PROPERTY(QString fileContent READ fileContent NOTIFY fileContentChanged)public:explicit FileHandler(QObject *parent = nullptr);QString fileName() const { return m_fileName; }bool isBinary() const { return m_isBinary; }bool fileModified() const { return m_fileModified; }QVariantList hexData() const { return m_hexData; }QString fileContent() const { return m_fileContent; }Q_INVOKABLE bool openFile(const QString &filePath);Q_INVOKABLE bool saveFile(const QString &content);Q_INVOKABLE bool saveBinaryFile(const QVariantList &hexData);Q_INVOKABLE void closeFile();Q_INVOKABLE bool isBinaryFile(const QString &filePath);signals:void fileNameChanged();void isBinaryChanged();void fileModifiedChanged();void hexDataChanged();void fileContentChanged();void fileOpened(const QString &fileName, bool isBinary);void fileSaved(const QString &fileName);void fileClosed();void errorOccurred(const QString &errorMessage);private:void processBinaryData(const QByteArray &data);QByteArray hexStringToByteArray(const QString &hexString);QString m_fileName;bool m_isBinary;bool m_fileModified;QByteArray m_originalData;QVariantList m_hexData;QString m_fileContent;
};#endif // FILEHANDLER_H

FileHandler.cpp文件源码

#include "filehandler.h"
#include <QDebug>
#include <QFileInfo>FileHandler::FileHandler(QObject *parent): QObject(parent), m_isBinary(false), m_fileModified(false)
{
}bool FileHandler::openFile(const QString &filePath)
{QFile file(filePath);if (!file.exists()) {emit errorOccurred(tr("文件不存在: %1").arg(filePath));return false;}if (!file.open(QIODevice::ReadOnly)) {emit errorOccurred(tr("无法打开文件: %1").arg(filePath));return false;}m_fileName = filePath;m_isBinary = isBinaryFile(filePath);if (m_isBinary) {// 读取二进制文件 - 使用更高效的方式QByteArray data = file.readAll();m_originalData = data;m_fileContent.clear();processBinaryData(data);} else {// 读取文本文件QTextStream stream(&file);stream.setCodec("UTF-8");m_fileContent = stream.readAll();m_originalData = m_fileContent.toUtf8();// 清空二进制数据m_hexData.clear();emit hexDataChanged();// 发送文件内容变化信号emit fileContentChanged();// 发送信号通知文件已打开emit fileOpened(filePath, false);}file.close();m_fileModified = false;emit fileNameChanged();emit isBinaryChanged();emit fileModifiedChanged();return true;
}bool FileHandler::saveFile(const QString &content)
{if (m_fileName.isEmpty()) {emit errorOccurred(tr("没有指定文件名"));return false;}QFile file(m_fileName);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {emit errorOccurred(tr("无法保存文件: %1").arg(m_fileName));return false;}QTextStream stream(&file);stream.setCodec("UTF-8");stream << content;file.close();m_fileContent = content;m_originalData = content.toUtf8();m_fileModified = false;emit fileContentChanged();emit fileSaved(m_fileName);emit fileModifiedChanged();return true;
}bool FileHandler::saveBinaryFile(const QVariantList &hexData)
{if (m_fileName.isEmpty()) {emit errorOccurred(tr("没有指定文件名"));return false;}QFile file(m_fileName);if (!file.open(QIODevice::WriteOnly)) {emit errorOccurred(tr("无法保存文件: %1").arg(m_fileName));return false;}// 将十六进制数据转换回二进制QByteArray binaryData;for (const QVariant &line : hexData) {QString hexLine = line.toString();QByteArray lineData = hexStringToByteArray(hexLine);binaryData.append(lineData);}file.write(binaryData);file.close();m_originalData = binaryData;m_fileModified = false;emit fileSaved(m_fileName);emit fileModifiedChanged();return true;
}void FileHandler::closeFile()
{m_fileName.clear();m_isBinary = false;m_fileModified = false;m_originalData.clear();m_hexData.clear();m_fileContent.clear();emit fileNameChanged();emit isBinaryChanged();emit fileModifiedChanged();emit hexDataChanged();emit fileContentChanged();emit fileClosed();
}bool FileHandler::isBinaryFile(const QString &filePath)
{QFileInfo fileInfo(filePath);QString extension = fileInfo.suffix().toLower();static const QStringList binaryExtensions = {"exe", "dll", "bin", "dat", "img", "so", "dylib","jpg", "jpeg", "png", "gif", "bmp", "ico","pdf", "doc", "docx", "xls", "xlsx","zip", "rar", "7z", "tar", "gz"};return binaryExtensions.contains(extension);
}void FileHandler::processBinaryData(const QByteArray &data)
{m_hexData.clear();// 预分配空间,提高性能int lineCount = (data.size() + 15) / 16; // 计算总行数m_hexData.reserve(lineCount);// 使用更高效的处理方式const uchar *bytes = reinterpret_cast<const uchar*>(data.constData());int dataSize = data.size();for (int i = 0; i < dataSize; i += 16) {QString hexLine;hexLine.reserve(48); // 预分配空间:16字节 * 3字符(2位十六进制+1空格)for (int j = 0; j < 16 && i + j < dataSize; j++) {uchar byte = bytes[i + j];// 构建十六进制字符串 - 修复的方法QString hexByte = QString("%1 ").arg(byte, 2, 16, QChar('0')).toUpper();hexLine.append(hexByte);}// 移除最后一个空格if (!hexLine.isEmpty() && hexLine.endsWith(' ')) {hexLine.chop(1);}// 创建行数据 - 只包含地址和十六进制数据QVariantMap lineData;lineData["address"] = QString("%1").arg(i, 8, 16, QChar('0')).toUpper();lineData["hex"] = hexLine;m_hexData.append(lineData);}emit hexDataChanged();emit fileOpened(m_fileName, true);
}QByteArray FileHandler::hexStringToByteArray(const QString &hexString)
{QByteArray byteArray;QStringList hexBytes = hexString.split(' ', Qt::SkipEmptyParts);byteArray.reserve(hexBytes.size());for (const QString &hexByte : hexBytes) {bool ok;char byte = static_cast<char>(hexByte.toInt(&ok, 16));if (ok) {byteArray.append(byte);}}return byteArray;
}

main.qml文件源码

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Dialogs 1.3
import QtQuick.Layouts 1.12
import FileEditor 1.0ApplicationWindow {id: mainWindowwidth: 800height: 600title: "文件编辑器 - " + (fileHandler.fileName ? fileHandler.fileName : "未命名")visible: trueFileHandler {id: fileHandleronFileOpened: {console.log("文件已打开:", fileName, "二进制:", isBinary)if (isBinary) {updateBinaryDisplay()} else {// 文本文件:直接将内容设置到 TextAreatextArea.text = fileHandler.fileContent}mainWindow.title = "文件编辑器 - " + fileName}onFileContentChanged: {// 当文件内容变化时更新显示(对于文本文件)if (!fileHandler.isBinary && fileHandler.fileName) {textArea.text = fileHandler.fileContent}}onFileSaved: {console.log("文件已保存:", fileName)infoDialog.text = "文件已保存: " + fileNameinfoDialog.open()}onFileClosed: {console.log("文件已关闭")textArea.text = ""mainWindow.title = "文件编辑器 - 未命名"}onErrorOccurred: {console.error("错误:", errorMessage)errorDialog.text = errorMessageerrorDialog.open()}onHexDataChanged: {if (fileHandler.isBinary) {updateBinaryDisplay()}}}// 更新二进制显示的函数function updateBinaryDisplay() {var displayText = ""for (var i = 0; i < fileHandler.hexData.length; i++) {var line = fileHandler.hexData[i]// 只显示地址和十六进制数据,移除 ASCII 部分displayText += line.address + "    " + line.hex + "\n"}textArea.text = displayText}// 菜单栏menuBar: MenuBar {Menu {title: "文件"MenuItem {text: "打开"onTriggered: openDialog.open()}MenuItem {text: "保存"onTriggered: {if (fileHandler.isBinary) {// 对于二进制文件,需要从显示的文本中提取十六进制数据var lines = textArea.text.split('\n')var hexData = []for (var i = 0; i < lines.length; i++) {if (lines[i].trim() === "") continuevar lineParts = lines[i].split('    ')if (lineParts.length >= 2) {hexData.push(lineParts[1].trim())}}fileHandler.saveBinaryFile(hexData)} else {fileHandler.saveFile(textArea.text)}}}MenuSeparator {}MenuItem {text: "关闭"onTriggered: fileHandler.closeFile()}MenuSeparator {}MenuItem {text: "退出"onTriggered: Qt.quit()}}Menu {title: "查看"MenuItem {text: "文本模式"enabled: fileHandler.fileName && fileHandler.isBinaryonTriggered: {// 重新以文本模式打开文件var currentFile = fileHandler.fileNamefileHandler.closeFile()fileHandler.openFile(currentFile)}}MenuItem {text: "十六进制模式"enabled: fileHandler.fileName && !fileHandler.isBinaryonTriggered: {// 重新以二进制模式打开文件var currentFile = fileHandler.fileNamefileHandler.closeFile()fileHandler.openFile(currentFile)}}}}// 主编辑区域ScrollView {anchors.fill: parentanchors.margins: 10TextArea {id: textAreawidth: parent.widthheight: parent.heightfont.family: fileHandler.isBinary ? "Courier New" : "Arial"font.pointSize: 10wrapMode: TextArea.WrapselectByMouse: trueplaceholderText: "请打开一个文件开始编辑..."// 监听文本变化,用于设置修改状态onTextChanged: {if (fileHandler.fileName && !fileHandler.isBinary) {// 可以在这里添加修改状态的逻辑}}}}// 状态栏footer: ToolBar {RowLayout {anchors.fill: parentLabel {text: {if (fileHandler.fileName) {if (fileHandler.isBinary) {return "十六进制模式 - " + fileHandler.fileName} else {return "文本模式 - " + fileHandler.fileName}} else {return "就绪"}}Layout.fillWidth: true}Label {text: "字符数: " + textArea.length}}}// 文件对话框FileDialog {id: openDialogtitle: "选择文件"selectMultiple: falsenameFilters: ["所有文件 (*)"]onAccepted: {var filePath = openDialog.fileUrl.toString().replace("file:///", "")fileHandler.openFile(filePath)}}// 信息对话框MessageDialog {id: infoDialogtitle: "信息"icon: StandardIcon.Information}// 错误对话框MessageDialog {id: errorDialogtitle: "错误"icon: StandardIcon.Critical}// 键盘快捷键Shortcut {sequence: "Ctrl+O"onActivated: openDialog.open()}Shortcut {sequence: "Ctrl+S"onActivated: {if (fileHandler.isBinary) {var lines = textArea.text.split('\n')var hexData = []for (var i = 0; i < lines.length; i++) {if (lines[i].trim() === "") continuevar lineParts = lines[i].split('    ')if (lineParts.length >= 2) {hexData.push(lineParts[1].trim())}}fileHandler.saveBinaryFile(hexData)} else {fileHandler.saveFile(textArea.text)}}}Shortcut {sequence: "Ctrl+W"onActivated: fileHandler.closeFile()}
}

main.cpp文件源码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "filehandler.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 设置应用信息app.setApplicationName("文件编辑器");app.setApplicationVersion("1.0");app.setOrganizationName("MyCompany");// 注册 C++ 类型到 QMLqmlRegisterType<FileHandler>("FileEditor", 1, 0, "FileHandler");QQmlApplicationEngine engine;// 加载 QML 文件const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl)QCoreApplication::exit(-1);}, Qt::QueuedConnection);engine.load(url);return app.exec();
}

三、效果演示

打开二进制文件的效果,可以进行编辑保存。

http://www.dtcms.com/a/453364.html

相关文章:

  • 添加最新的LSKNet遥感目标检测网络主干
  • 融资网站开发湖南二维码标签品牌
  • 【开源】基于STM32的智能骑行头盔设计
  • 【Python刷力扣hot100】49. Group Anagrams
  • 招聘网站大全专业的企业进销存软件定制
  • 绿色学校网站模板高明网站建设报价
  • Ubuntu 22.04 + Ryu/Mininet:跨越 Python 3.10 依赖“死亡三角”的完美配置指南
  • AI智能体(Agent)大模型入门【6】--编写fasteAPI后端请求接口实现页面聊天
  • 广西 南宁 微信微网站开发虚拟主机使用教程
  • 电子商务网站开发 当当网网站优化关键词怎么做
  • 学习日报 20251007|深度解析:基于 Guava LoadingCache 的优惠券模板缓存设计与实现
  • 什么是MOE?
  • 大模型-扩散模型(Diffusion Model)原理讲解(4
  • 【深度学习新浪潮】入门Flash Attention:从原理到Python手搓实现
  • 不做“KPI牛马“,回归真生活——双节沉思录
  • Java接口中实现多线程并行处理,大数据量查询实战,成倍提效、性能分析,笔记01
  • AI学习日记——参数的初始化
  • 数字信号处理 第七章(FIR数字滤波器设计)
  • 网站建设公司实力网站建设死人接单
  • 河南住房与建设厅网站杭州网站建设交易
  • 岳池建设局网站什么是网络设计的前提
  • GDB Stub定义及命名由来解析
  • 男人最爱上的做网站网页制作开版费
  • FreeRTOS_API模块综合应用篇(八)
  • tuchuang_myfilesshare文件列表_共享文件
  • GJOI 10.4/10.5 题解
  • C语言入门教程(第2讲):数据类型与变量详解与实战讲解
  • 哪些网站建设公司wordpress悬浮联系表
  • 5g互联如何取消网站备案中山seo代理商
  • 生成式人工智能对学习生态的重构:从“辅助工具”到“依赖风险”的平衡难题