「Qt」文件读写操作

0、引言

        我们知道 C 和 C++ 都提供了文件读写的类库,不过 Qt 也有一套自己的文件读写操作;本文主要介绍 Qt 中进行文件读写操作的类 —— QFile

1、QFileDialog 文件对话框

        一般的桌面应用程序,当我们想要打开一个文件时,通常会弹出一个文件对话框。在 Qt 中,文件对话框使用 QFileDialog 类实现。

        QFileDialog 类允许用户遍历文件系统,以便选择一个或多个文件或目录。

        最简单的创建 QFileDialog 的方法是使用静态函数:

fileName = QFileDialog::getOpenFileName(this,
    tr("Open Image"), "/home", tr("Image Files (*.png *.jpg *.bmp)"));

        tr() 是 Qt 中的一个函数,用于将字符串标记为可翻译的字符串。这个函数的作用是在运行时,根据配置在 Qt Linguist 工具中的翻译,将输入字符串翻译成适当的语言。

        这意味着,如果您的应用程序支持多种语言,并且在 Qt Linguist 工具中配置了相应的翻译,那么这些字符串将在运行时被翻译成用户选择的语言。

        在上面的例子中,我们使用静态方法创建了一个模态 QFileDialog(什么是 模态对话框?)。该对话框初始显示 "/home" 目录中的内容,并显示与字符串 "Image files (*.png *.jpg *.bmp)" 中给出的模式匹配的文件。文件对话框的父窗口设置为 this,窗口标题设置为 "Open Image"

        如果你想要使用多个过滤器,请用 两个分号 分隔每一个过滤器。例如:

"Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML files (*.xml)"

        如果您想要不使用任何文件过滤器,请直接传递空字符串。或者传递

"All Files (*)"

        (了解更多...)

        在上面的例子中我们可以尝试使用 QFileDialog 选择一个文件并将 fileName 打印到单行文本框,最终实现效果类似于下图:

文件对话框

2、QFile

        QFileDialog 只是帮助我们完成了获取文件的绝对路径(什么是 绝对路径(以 Windows 系统为例)?)的工作;接下来才是要进行文件读写的操作。在 Qt 中,文件读写通常与 QFile 类相关。

        💬 有关 QFile 的用法,还请读者参考 QFile 的官方文档,这里笔者不再赘述。下面主要介绍 QFileDialog 类配合 QFile 类的使用案例:

2.1、QFile 读操作

        以下代码展示了 QFileDialog 类配合 QFile 类打开并读取一张图片文件(我们在与 QPushButton::clicked 信号关联的槽函数中读取图片文件):

//点击“打开”按钮,弹出文件选择对话框
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open Image"), "/home" , tr("Image Files (*.png *.jpg *.bmp *.svg)"));
    //将路径放到 LineEdit 中
    ui->lineEdit->setText(fileName);

    //使用文件的绝对路径实例化 QFile
    QFile file(fileName);
    //只读模式打开文件
    file.open(QIODevice::ReadOnly);
    //全部读取
    QByteArray bytes = file.readAll();
    //生成 QImage 实例
    QImage image = QImage::fromData(bytes);
    //生成 QPixmap 实例并显示在 QLabel 控件上
    ui->label->setPixmap(QPixmap::fromImage(image));
    //关闭文件
    file.close();
});

        使用 QFileDialog 配合 QFile,我们可以做到在运行时选择并读取图片文件:

读取图片文件并显示


        此外,我们还可以尝试使用 QFileDialog 配合 QFile 打开文本文件,只需略微修改上面示例代码中的槽函数即可:

  •  在 Linux(Ubuntu) 下,你可以使用如下命令创建文本文件:
    touch filename.txt
  • 创建完成后,在其中编辑一些字符,保存后就可以使用如下命令查看该文本文件的字符编码格式:
    file filename.txt
    
    # 如果文本文件中只有英文字符,则编码格式为 ASCII;
    # 如果带有中文字符,则编码格式为 UTF-8 Unicode。
  • 使用如下命令转换文本文件的字符编码格式(参考链接):
    sudo iconv -f 'utf-8' -t 'gbk' filename.txt > GbkText.txt
//点击“打开”按钮,弹出文件选择对话框
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open Text File"), "/home" , tr("Text Files (*.txt)"));
    //将路径放到 LineEdit 中
    ui->lineEdit->setText(fileName);

    //使用文件的绝对路径实例化 QFile
    QFile file(fileName);
    //只读模式打开文件
    file.open(QIODevice::ReadOnly);
    //全部读取
    QByteArray bytes = file.readAll();
    //在 QLabel 文件上显示文本
    ui->label->setText(bytes);
    //关闭文件
    file.close();
});

        需要注意的是,QLabel::SetText() 方法接受一个 QString 类型的参数,而在上面的示例代码中我们传递了一个字节数组作为参数。

        巧合的是,QString 类提供了一个接受 QByteArray 类型的构造函数,这意味着这里会发生一次隐式转换将 QByteArray 类型转换为 QString 类型。不过,这个构造函数的转换将会使用 UTF-8 格式解码字符串。

        这样的隐式转换可能会导致一些问题,特别是当你的文本文件不是 UTF-8 格式编码时:

文本文件编码问题
两个文本文件内容一致,但 'GBK' 字符编码格式文件会显示乱码

        为了避免这样的问题,我们可以尝试使用一些 Qt 中专门用于编码和解码文本的类,比如 QTextCodec在 Qt 5.15 过后已经部分弃用)和 QStringConverter 类;或者使用流来读取文件(使用流的时候,默认情况下,QTextStream 假设文件以 UTF-8 编码;但这可以使用QTextStream::setEncoding() 来改变)。

2.2、QFile 写操作

        除了读操作,我们还可以使用 QFileDialog 类配合 QFile 类打开并向文件中写入数据。下面是一个打开文本文件并向其中追加新的文本的示例,同样略微修改上面示例代码中的槽函数即可:

//点击“打开”按钮,弹出文件选择对话框
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open Text File"), "/home" , tr("Text Files (*.txt)"));
    //将路径放到 LineEdit 中
    ui->lineEdit->setText(fileName);

    //使用文件的绝对路径实例化 QFile
    QFile file(fileName);
    //以追加的模式打开文件
    file.open(QIODevice::Append);
    //写数据
    file.write("这是我写入的文本!\n");
    //关闭文件
    file.close();

    //重新以只读方式打开文件
    file.open(QIODevice::ReadOnly);
    QByteArray data;
    //当没有到达文件末尾时
    while(!file.atEnd()){
        //按行读取
        data += file.readLine();
    }
    ui->label->setText(data);
    //关闭文件
    file.close();
});

下面是运行效果:

QFile 写操作

3、QFileInfo 文件信息

        QFileInfo 提供了文件在文件系统中的名称和位置(路径)、访问权限以及是目录还是符号链接等信息,还有文件的大小、最后修改/读取时间等信息。此外,QFileInfo 也可以用来获取关于 Qt 资源的信息。

        QFileInfo 可以指向具有相对或绝对文件路径的文件。绝对文件路径以目录分隔符 "/" 开头(或在 Windows 上以驱动器规格开头)。相对文件名以目录名或文件名开头,并指定相对于当前工作目录的路径。绝对路径的一个例子是字符串 "/tmp/quartz"。相对路径可能看起来像 "src/fatlib"。可以使用函数 isRelative() 检查 QFileInfo 使用的是相对文件路径还是绝对文件路径。您可以调用 makeAbsolute() 函数将 QFileInfo 的相对路径转换为绝对路径。

⚠️ 注意:以冒号(:)开头的路径总是被认为是绝对路径,因为它们表示一个 QResource

        QFileInfo 处理的文件是在构造函数中设置的(或者后续使用 setFile() 设置)。使用 exists() 查看文件是否存在并使用 size() 获取其大小。

        文件的类型通过 isFile()isDir()isSymLink() 获得。symLinkTarget() 函数提供了符号链接指向的文件的名称。

        在 Unix(包括 macOS 和 iOS)上,该类中的属性获取器函数返回目标文件的时间和大小等属性,而不是符号链接的时间和大小等属性,这是因为 Unix 透明地处理符号链接。使用 QFile 打开符号链接可以有效地打开链接的目标。例如:

#ifdef Q_OS_UNIX

QFileInfo info1("/home/bob/bin/untabify");
info1.isSymLink();          // returns true
info1.absoluteFilePath();   // returns "/home/bob/bin/untabify"
info1.size();               // returns 56201
info1.symLinkTarget();      // returns "/opt/pretty++/bin/untabify"

QFileInfo info2(info1.symLinkTarget());
info2.isSymLink();          // returns false
info2.absoluteFilePath();   // returns "/opt/pretty++/bin/untabify"
info2.size();               // returns 56201

#endif

        在 Windows 上,快捷方式(.lnk 文件)目前被视为符号链接。与 Unix 系统一样,属性获取器返回目标文件的大小,而不是 .lnk 文件本身。此行为已被弃用,可能会在 Qt 的未来版本中删除,之后 .lnk 文件将被视为常规文件。

#ifdef Q_OS_WIN

QFileInfo info1("C:\\Users\\Bob\\untabify.lnk");
info1.isSymLink();          // returns true
info1.absoluteFilePath();   // returns "C:/Users/Bob/untabify.lnk"
info1.size();               // returns 63942
info1.symLinkTarget();      // returns "C:/Pretty++/untabify"

QFileInfo info2(info1.symLinkTarget());
info2.isSymLink();          // returns false
info2.absoluteFilePath();   // returns "C:/Pretty++/untabify"
info2.size();               // returns 63942

#endif

        文件名称的元素可以使用 path()fileName() 提取。fileName() 的部分可以用 baseName()suffix() 或 completeSuffix() 提取。QFileInfo 对象到由 Qt 类创建的目录将不会有尾随文件分隔符。如果您希望在自己的文件信息对象中使用尾随分隔符,只需在构造函数或 setFile() 给出的文件名后附加一个。

        文件的日期由 birthTime()lastModified()lastRead()fileTime() 返回。通过 isReadable()isWritable()isExecutable() 获取文件的访问权限信息。文件的所有权可以从 owner()ownerId()group()groupId() 中获得。您可以使用 permission() 函数在单个语句中检查文件的权限和所有权。

⚠️ 注意:在 NTFS 文件系统上,出于性能原因,默认情况下禁用所有权和权限检查。要启用它,包括以下行:

extern Q_CORE_EXPORT int qt_ntfs_permission_lookup;

然后通过对 qt_ntfs_permission_lookup 加 1 和减 1 来打开和关闭权限检查。

qt_ntfs_permission_lookup++;     //启用检查
qt_ntfs_permission_lookup--;     //再次关闭检查

⚠️ 注意:由于这是一个非原子全局变量,所以只有在除主线程以外的任何线程启动之前或除主线程以外的每个线程结束之后,才可以对 qt_ntfs_permission_lookup 进行递增或递减操作。


        下面是一个 QFileDialog 类配合 QFileInfo 类获取文件信息的代码示例,同样略微修改 2.1 小节示例代码中的槽函数即可:

//点击“打开”按钮,弹出文件选择对话框
connect(ui->pushButton, &QPushButton::clicked, this, [=](){
    QString fileName = QFileDialog::getOpenFileName(this,
        tr("Open File"), "/home", tr("All Files (*)"));
    //将路径放到 LineEdit 中
    ui->lineEdit->setText(fileName);

    QFileInfo info(fileName);
    QString str = "path() = " + info.path() + "\n"
                  + "filePath() = " + info.filePath() + "\n"
                  + "fileName() = " + info.fileName() + "\n"
                  + "baseName() = " + info.baseName() + "\n"
                  + "suffix() = " + info.suffix() + "\n"
                  + "completeSuffix() = " + info.completeSuffix() + "\n"
                  + "birthTime() = " + info.birthTime().toString();    //toString()方法可指定日期输出格式
    ui->label->setText(str);
});

💬 在打印文件的日期信息时,可以指定输出的格式,比如:

info.birthTime().toString("yyyy/MM/dd hh:mm:ss"); // 2023/08/16 21:12:34

(参考 QDateTimetoString() 方法了解更多)

运行效果:

QFileInfo 获取文件信息

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Qt是一个跨平台的C++应用程序开发框架,提供了多种功能模块和工具类,包括打开文件选择对话框。该对话框的实现可以通过QFileDialog类来完成。我们可以使用该类的静态函数getOpenFileName或getOpenFileNames来打开文件选择对话框,分别用于选择单个文件或多个文件。其函数原型如下: QString QFileDialog::getOpenFileName(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFlags<Options> options = QFlags<Options>()) QStringList QFileDialog::getOpenFileNames(QWidget *parent = nullptr, const QString &caption = QString(), const QString &dir = QString(), const QString &filter = QString(), QString *selectedFilter = nullptr, QFlags<Options> options = QFlags<Options>()) 其中,参数parent表示父窗口,可以为nullptr;参数caption表示对话框标题,可以为空;参数dir表示对话框打开时的默认路径,可以为空,表示系统默认路径;参数filter表示对话框筛选文件的类型,例如 "*.txt" 表示只显示txt文件;参数selectedFilter表示默认选中的文件类型;参数options表示对话框的选项,例如默认选择的文件名等等。返回值为QString或QStringList类型,表示选择的文件路径或文件列表。 具体使用方法如下: QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), QDir::currentPath(), tr("Text Files (*.txt);;All Files(*)")); QStringList filenames = QFileDialog::getOpenFileNames(this, tr("Open Files"), QDir::currentPath(),tr("Text Files (*.txt);;All Files(*)")); 其中,参数this表示当前窗口,tr表示对字符串进行国际化处理,QDir::currentPath()表示当前路径。选中的文件路径或文件列表会存储在filename或filenames中。通过上述的两个静态函数,我们可以非常方便地实现Qt打开文件选择对话框。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值