【从零开始的Qt开发指南】(十九)Qt 文件操作:从 I/O 设备到文件信息,一站式掌握跨平台文件处理


目录

​编辑

前言

[一、Qt 文件操作核心认知](#一、Qt 文件操作核心认知)

[1.1 为什么选择 Qt 文件 API?](#1.1 为什么选择 Qt 文件 API?)

[1.2 Qt 文件操作核心类关系](#1.2 Qt 文件操作核心类关系)

二、输入输出设备类(QIODevice):文件操作的基石

[2.1 QIODevice 核心作用](#2.1 QIODevice 核心作用)

[2.2 核心 API 速览](#2.2 核心 API 速览)

[2.2.1 打开与关闭](#2.2.1 打开与关闭)

[2.2.2 数据读写](#2.2.2 数据读写)

[2.2.3 状态判断](#2.2.3 状态判断)

[2.3 关键打开模式(OpenMode)](#2.3 关键打开模式(OpenMode))

[2.4 常用 I/O 设备类详解](#2.4 常用 I/O 设备类详解)

[2.4.1 QFile:本地文件操作主力](#2.4.1 QFile:本地文件操作主力)

[2.4.2 QSaveFile:安全保存文件](#2.4.2 QSaveFile:安全保存文件)

[2.4.3 QTemporaryFile:临时文件创建](#2.4.3 QTemporaryFile:临时文件创建)

[2.4.4 QBuffer:内存缓冲区 I/O](#2.4.4 QBuffer:内存缓冲区 I/O)

三、文件读写类(QFile):实战核心

[3.1 案例 1:读取文本文件(完整读取)](#3.1 案例 1:读取文本文件(完整读取))

[步骤 1:创建 Qt 项目](#步骤 1:创建 Qt 项目)

[步骤 2:设计 UI 界面](#步骤 2:设计 UI 界面)

[步骤 3:头文件(widget.h)声明](#步骤 3:头文件(widget.h)声明)

[步骤 4:源文件(widget.cpp)实现](#步骤 4:源文件(widget.cpp)实现)

运行效果

关键说明

[3.2 案例 2:写入文本文件(追加模式)](#3.2 案例 2:写入文本文件(追加模式))

[步骤 1:复用上述 UI 界面](#步骤 1:复用上述 UI 界面)

[步骤 2:修改 widget.cpp 中的槽函数](#步骤 2:修改 widget.cpp 中的槽函数)

运行效果

关键说明

四、文件和目录信息类(QFileInfo):获取文件元数据

[4.1 QFileInfo 核心 API](#4.1 QFileInfo 核心 API)

[4.1.1 基本信息查询](#4.1.1 基本信息查询)

[4.1.2 类型判断](#4.1.2 类型判断)

[4.1.3 时间信息查询](#4.1.3 时间信息查询)

[4.1.4 权限查询](#4.1.4 权限查询)

[4.2 案例:文件信息查询工具](#4.2 案例:文件信息查询工具)

[步骤 1:UI 设计](#步骤 1:UI 设计)

[步骤 2:实现文件信息查询逻辑](#步骤 2:实现文件信息查询逻辑)

运行效果

关键说明

五、高级应用:文件操作的最佳实践与避坑指南

[5.1 编码问题处理](#5.1 编码问题处理)

[示例:读取 GBK 编码的文本文件](#示例:读取 GBK 编码的文本文件)

[5.2 大文件断点续传思路](#5.2 大文件断点续传思路)

示例:断点续传核心代码

[5.3 常见坑与解决方案](#5.3 常见坑与解决方案)

[坑 1:文件路径包含中文或空格导致打开失败](#坑 1:文件路径包含中文或空格导致打开失败)

[坑 2:忘记关闭文件导致资源泄漏](#坑 2:忘记关闭文件导致资源泄漏)

[坑 3:二进制文件使用文本模式打开导致损坏](#坑 3:二进制文件使用文本模式打开导致损坏)

[坑 4:readAll()读取大文件导致内存溢出](#坑 4:readAll()读取大文件导致内存溢出)

[坑 5:跨平台路径分隔符问题](#坑 5:跨平台路径分隔符问题)

总结


前言

在 Qt 开发中,文件操作是贯穿项目全生命周期的核心功能 ------ 无论是配置文件读写、日志记录、数据导入导出,还是资源加载,都离不开对文件系统的精准操控。Qt 作为跨平台框架,封装了一套强大且统一的文件操作 API,彻底解决了不同操作系统下文件路径、编码格式、I/O 逻辑不一致的痛点。本文将聚焦 Qt 文件操作的四大核心模块(文件概述、I/O 设备类、文件读写、文件目录信息),手把手教你吃透从基础到进阶的所有文件操作技巧,让你的跨平台文件处理代码既简洁又稳健!下面就让我们正式开始吧!


一、Qt 文件操作核心认知

1.1 为什么选择 Qt 文件 API?

传统 C/C++ 的文件操作(如fopenfreadWriteFile)存在明显短板:需要手动处理跨平台差异(如 Windows 的\和 Linux 的/路径分隔符)、编码转换复杂、缺乏面向对象的封装。而 Qt 的文件类库则完美解决了这些问题:

  • 跨平台兼容:一套代码适配 Windows、Linux、macOS 等系统,Qt 自动处理路径分隔符、换行符等差异;
  • 面向对象设计 :通过QFileQFileInfo等类封装文件操作,接口直观易用;
  • 功能全面:支持文件读写、目录遍历、文件信息查询、临时文件创建等全场景需求;
  • 无缝集成 Qt 生态 :可与QByteArrayQStringQDateTime等类快速交互,简化数据处理流程。

1.2 Qt 文件操作核心类关系

Qt 的文件操作类基于统一的继承体系,核心继承关系如下:

  • QIODevice :所有 I/O 设备的抽象基类,定义了读写数据的通用接口(如read()write()open());
  • QFileDevice :文件设备的基类,提供文件特有的操作(如fileName()filePermissions());
  • QFile:最常用的文件操作类,直接操作本地文件;
  • 其他子类:针对特定 I/O 设备(如网络 socket、串口、蓝牙),本文重点聚焦本地文件操作。

二、输入输出设备类(QIODevice):文件操作的基石

2.1 QIODevice 核心作用

QIODevice是 Qt 中所有输入输出设备的 "万能接口",无论是文件、网络 socket,还是内存缓冲区,都通过它提供的统一 API 进行数据读写。它的核心价值在于:屏蔽不同 I/O 设备的底层差异,让开发者用相同的逻辑处理各种数据输入输出场景

2.2 核心 API 速览

2.2.1 打开与关闭

  • bool open(OpenMode mode):打开设备,mode指定打开模式(如只读、只写、追加等);
  • void close():关闭设备,必须在操作完成后调用,避免资源泄漏;
  • bool isOpen() const:判断设备是否已打开;
  • OpenMode openMode() const:获取当前设备的打开模式。

2.2.2 数据读写

  • QByteArray read(qint64 maxSize):读取最多maxSize字节的数据,返回读取到的字节数组;
  • QByteArray readAll():读取设备中所有剩余数据,适用于小文件;
  • qint64 readLine(char *data, qint64 maxSize):读取一行数据(直到换行符);
  • qint64 write(const QByteArray &byteArray):写入字节数组,返回实际写入的字节数;
  • qint64 write(const char *data, qint64 maxSize):写入指定长度的字符数组。

2.2.3 状态判断

  • bool atEnd() const:判断是否到达设备末尾;
  • qint64 bytesAvailable() const:返回当前可读取的字节数;
  • bool canReadLine() const:判断是否可以读取一行数据。

2.3 关键打开模式(OpenMode)

QIODevice的打开模式通过**QIODevice::OpenModeFlag**枚举定义,常用模式如下(可通过|组合使用):

模式枚举 说明 适用场景
QIODevice::NotOpen 未打开(默认状态) -
QIODevice::ReadOnly 只读模式 读取配置文件、日志文件
QIODevice::WriteOnly 只写模式 新建文件并写入数据
QIODevice::ReadWrite 读写模式 既要读又要写的场景(如修改文件内容)
QIODevice::Append 追加模式 日志记录(在文件末尾添加内容)
QIODevice::Truncate 截断模式 覆盖文件(打开时清空原有内容)
QIODevice::Text 文本模式 文本文件读写(自动转换换行符)
QIODevice::Unbuffered 无缓冲模式 实时性要求高的场景(绕过缓冲区)
QIODevice::NewOnly 仅新建模式 确保文件不存在(存在则打开失败)

注意WriteOnly模式默认隐含Truncate(截断),若需保留原有内容并追加,需组合Append模式(WriteOnly | Append)。

2.4 常用 I/O 设备类详解

2.4.1 QFile:本地文件操作主力

QFile是**QFileDevice**的直接子类,专门用于操作本地文件系统中的文件。支持文件路径的自动解析(跨平台适配),核心优势:

  • 支持绝对路径(如C:/test.txt/home/user/test.txt)和相对路径;
  • 可与QFileInfo配合获取文件详细信息;
  • 支持文件权限设置、文件重命名、删除等操作。

2.4.2 QSaveFile:安全保存文件

QSaveFile是**QFile**的子类,专为 "安全保存" 设计,核心特点:

  • 先将数据写入临时文件,成功后再替换目标文件;
  • 若保存过程中出现错误(如断电、程序崩溃),目标文件不会被损坏;
  • 适用于重要文件(如配置文件、数据库文件)的保存。

2.4.3 QTemporaryFile:临时文件创建

QTemporaryFile用于创建临时文件,核心优势:

  • 自动生成唯一文件名,避免冲突;
  • 临时文件默认在程序退出或对象销毁时自动删除;
  • 支持随机访问,可作为临时缓冲区使用。

2.4.4 QBuffer:内存缓冲区 I/O

QBuffer将**QByteArray**作为内存缓冲区,模拟文件 I/O 操作,核心用途:

  • 数据临时存储(如网络数据接收后缓存);
  • 避免频繁读写本地文件,提升性能;
  • 可与其他 Qt 类(如QDataStream)配合使用。

三、文件读写类(QFile):实战核心

QFile是 Qt 文件操作中最常用的类,几乎所有本地文件操作都离不开它。下面通过多个实战案例,详解文件读写的常见场景和最佳实践。

3.1 案例 1:读取文本文件(完整读取)

实现功能:通过按钮选择文本文件,读取全部内容并显示在文本框中。

步骤 1:创建 Qt 项目

新建 Qt Widgets Application 项目,基类选择QWidget,勾选 "Generate form"。

步骤 2:设计 UI 界面

打开widget.ui,拖入以下控件:

  • QLineEdit(命名为lineEdit):显示选中的文件路径;
  • QPushButton(命名为btn,文本改为 "选择并读取文件"):触发文件选择和读取;
  • QTextEdit(命名为textEdit):显示读取到的文件内容;
  • 布局 :使用垂直布局,lineEditbtn放在水平布局中,再与textEdit组合。

步骤 3:头文件(widget.h)声明

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFile>
#include <QFileDialog>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private slots:
    // 按钮点击槽函数
    void on_btn_clicked();

private:
    Ui::Widget *ui;
};

#endif // WIDGET_H

步骤 4:源文件(widget.cpp)实现

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    this->setWindowTitle("Qt文件读取示例");
    // 设置文本框为只读模式
    ui->textEdit->setReadOnly(true);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::on_btn_clicked()
{
    // 弹出文件选择对话框,过滤文本文件
    QString filePath = QFileDialog::getOpenFileName(
        this,                  // 父窗口
        "选择文本文件",          // 对话框标题
        "C:/Users/Lenovo/Desktop", // 默认路径
        "文本文件 (*.txt *.cpp *.h);;所有文件 (*.*)" // 文件过滤器
    );

    if (filePath.isEmpty())
    {
        qDebug() << "用户取消了文件选择";
        return;
    }

    // 显示文件路径
    ui->lineEdit->setText(filePath);

    // 实例化QFile对象
    QFile file(filePath);

    // 以只读+文本模式打开文件
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        qDebug() << "文件打开失败:" << file.errorString();
        return;
    }

    // 读取全部内容(适用于小文件)
    QByteArray fileContent = file.readAll();

    // 将字节数组转换为字符串,显示到文本框
    ui->textEdit->setText(QString::fromUtf8(fileContent));

    // 关闭文件(必须调用,释放资源)
    file.close();

    qDebug() << "文件读取成功,大小:" << fileContent.size() << "字节";
}

运行效果

  • 点击 "选择并读取文件" 按钮,弹出文件选择对话框;
  • 选择一个文本文件(如.txt.cpp),文件路径会显示在lineEdit中;
  • 文本文件的内容会完整显示在textEdit中,控制台输出文件大小。

关键说明

  • QFileDialog::getOpenFileName:弹出文件选择对话框,返回选中的文件路径(用户取消则返回空字符串);
  • **file.open()**返回bool类型,必须判断是否打开成功(避免文件不存在、权限不足等错误);
  • **readAll()**适合小文件,大文件建议使用readLine()逐行读取,避免占用过多内存;
  • QString::fromUtf8():假设文件编码为 UTF-8,若为 GBK 编码,需使用QTextCodec转换(后续案例详解)。

3.2 案例 2:写入文本文件(追加模式)

实现功能:在选中的文件末尾追加一段文本内容。

步骤 1:复用上述 UI 界面

只需将btn的文本改为 "选择并追加内容",textEdit改为可编辑模式(取消readOnly)。

步骤 2:修改 widget.cpp 中的槽函数

cpp 复制代码
void Widget::on_btn_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(
        this,
        "选择要追加内容的文件",
        "C:/Users/Lenovo/Desktop",
        "文本文件 (*.txt);;所有文件 (*.*)"
    );

    if (filePath.isEmpty())
    {
        qDebug() << "用户取消了文件选择";
        return;
    }

    ui->lineEdit->setText(filePath);

    QFile file(filePath);

    // 以只写+追加+文本模式打开文件
    if (!file.open(QIODevice::WriteOnly | QIODevice::Append | QIODevice::Text))
    {
        qDebug() << "文件打开失败:" << file.errorString();
        return;
    }

    // 获取文本框中的内容(用户输入的要追加的内容)
    QString appendContent = ui->textEdit->toPlainText();
    if (appendContent.isEmpty())
    {
        qDebug() << "追加内容为空,取消写入";
        file.close();
        return;
    }

    // 追加换行符(避免内容粘连)
    appendContent += "\n【这是示例!!!】\n";

    // 将字符串转换为字节数组并写入
    qint64 writeBytes = file.write(appendContent.toUtf8());

    if (writeBytes != -1)
    {
        qDebug() << "追加成功,写入字节数:" << writeBytes;
        // 清空文本框
        ui->textEdit->clear();
        QMessageBox::information(this, "成功", "内容追加到文件末尾!");
    }
    else
    {
        qDebug() << "写入失败:" << file.errorString();
        QMessageBox::warning(this, "失败", "内容追加失败!");
    }

    // 关闭文件
    file.close();
}

运行效果

  • textEdit中输入要追加的内容;
  • 选择目标文件后,内容会被追加到文件末尾,同时添加标记文本;
  • 弹出提示框告知操作结果,控制台输出写入的字节数。

关键说明

  • WriteOnly | Append模式:打开文件后不会清空原有内容,新内容追加在末尾;
  • **write()**返回实际写入的字节数,-1 表示写入失败;
  • 追加换行符是良好的编程习惯,避免不同内容粘连在一起。

四、文件和目录信息类(QFileInfo):获取文件元数据

QFileInfo是 Qt 提供的用于获取文件和目录信息的工具类,支持查询文件名、大小、创建时间、修改时间、权限等元数据,无需手动解析文件路径或调用系统 API。

4.1 QFileInfo 核心 API

4.1.1 基本信息查询

  • QString fileName() const:获取文件名(含后缀,如test.txt);
  • QString baseName() const:获取文件名(不含后缀,如test);
  • QString suffix() const:获取文件后缀(如txtpng);
  • QString completeSuffix() const:获取完整后缀(如tar.gz);
  • QString filePath() const:获取文件完整路径(如C:/test.txt);
  • QString path() const:获取文件所在目录路径(如C:/);
  • qint64 size() const:获取文件大小(字节)。

4.1.2 类型判断

  • bool isFile() const:判断是否为文件;
  • bool isDir() const:判断是否为目录;
  • bool isSymLink() const:判断是否为符号链接;
  • bool isExecutable() const:判断是否为可执行文件;
  • bool exists() const:判断文件 / 目录是否存在。

4.1.3 时间信息查询

  • QDateTime created() const:获取文件创建时间;
  • QDateTime lastModified() const:获取文件最后修改时间;
  • QDateTime lastRead() const:获取文件最后访问时间。

4.1.4 权限查询

  • bool isReadable() const:判断是否可读;
  • bool isWritable() const:判断是否可写;
  • bool isExecutable() const:判断是否可执行。

4.2 案例:文件信息查询工具

实现功能:选择文件后,显示该文件的详细信息(名称、路径、大小、创建时间、修改时间等)。

步骤 1:UI 设计

  • QLineEdit:显示文件路径;
  • QPushButton:选择文件;
  • QTextEdit:显示文件详细信息;
  • 布局:垂直布局,确保界面整洁。

步骤 2:实现文件信息查询逻辑

cpp 复制代码
void Widget::on_queryBtn_clicked()
{
    QString filePath = QFileDialog::getOpenFileName(
        this,
        "选择要查询的文件",
        "C:/Users/Lenovo/Desktop",
        "所有文件 (*.*)"
    );

    if (filePath.isEmpty())
        return;

    ui->lineEdit->setText(filePath);

    // 实例化QFileInfo对象(传入文件路径)
    QFileInfo fileInfo(filePath);

    // 构建文件信息字符串
    QString infoStr;
    infoStr += "=== 文件详细信息 ===\n";
    infoStr += QString("文件名为:%1\n").arg(fileInfo.fileName());
    infoStr += QString("后缀名为:%1\n").arg(fileInfo.baseName());
    infoStr += QString("文件大小为:%1 (%2 KB)")
    infoStr += QString("文件路径为:%1\n").arg(fileInfo.filePath());
    infoStr += QString("文件大小:%1\n").arg(fileInfo.suffix());
    infoStr += QString("是否为文件:%1\n").arg(fileInfo.isFile() ? "true" : "false");
    infoStr += QString("创建时间为:%1\n").arg(fileInfo.created().toString("yyyy-MM-dd hh:mm:ss"));
    infoStr += QString("是否为目录:%1\n").arg(fileInfo.isDir() ? "true" : "false");

    // 显示信息
    ui->textEdit->setText(infoStr);
}

运行效果

  • 选择任意文件后,文本框会显示该文件的完整信息,包括大小(转换为 KB 并保留两位小数)、时间(格式化显示)、权限等;
  • 支持所有文件类型,无需区分文本或二进制文件。

关键说明

  • QFileInfo无需打开文件即可获取大部分信息(如名称、路径、大小、时间),效率高;
  • 时间格式化:toString("yyyy-MM-dd hh:mm:ss")QDateTime转换为易读的字符串格式;
  • 文件大小转换:通过fileInfo.size() / 1024.0将字节转换为 KB,'f', 2表示保留两位小数。

五、高级应用:文件操作的最佳实践与避坑指南

5.1 编码问题处理

Qt 默认使用 UTF-8 编码,但 Windows 系统中很多文件使用 GBK 编码,直接读写会导致乱码。解决方案:使用QTextCodec进行编码转换。

示例:读取 GBK 编码的文本文件

cpp 复制代码
#include <QTextCodec>

void Widget::readGbkFile()
{
    QString filePath = QFileDialog::getOpenFileName(this, "选择GBK文件", "", "文本文件 (*.txt)");
    if (filePath.isEmpty())
        return;

    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
        return;

    // 读取原始字节数据
    QByteArray gbkData = file.readAll();
    file.close();

    // 设置GBK编码
    QTextCodec *codec = QTextCodec::codecForName("GBK");
    if (!codec)
    {
        qDebug() << "不支持GBK编码";
        return;
    }

    // 将GBK字节数组转换为UTF-8字符串
    QString content = codec->toUnicode(gbkData);
    ui->textEdit->setText(content);
}

5.2 大文件断点续传思路

对于超大文件(如几十 MB、GB 级),可采用断点续传,核心思路:

  1. 每次写入时记录已写入的字节数(存储在配置文件或数据库中);
  2. 下次写入时,使用**file.seek(offset)**将文件指针移动到已写入的位置;
  3. 继续写入剩余数据。

示例:断点续传核心代码

cpp 复制代码
void Widget::resumeWriteFile(const QString &filePath, const QByteArray &data, qint64 offset)
{
    QFile file(filePath);
    if (!file.open(QIODevice::WriteOnly | QIODevice::Append))
        return;

    // 移动文件指针到指定位置(断点位置)
    if (!file.seek(offset))
    {
        qDebug() << "文件指针移动失败";
        file.close();
        return;
    }

    // 写入数据
    qint64 writeBytes = file.write(data);
    file.close();

    if (writeBytes != -1)
    {
        qDebug() << "断点续传成功,写入" << writeBytes << "字节,当前总偏移:" << offset + writeBytes;
        // 更新配置文件中的偏移量
        saveOffset(filePath, offset + writeBytes);
    }
}

5.3 常见坑与解决方案

坑 1:文件路径包含中文或空格导致打开失败

  • 原因:Qt5 及以上默认支持 Unicode 路径,但若手动拼接路径时未使用QString,可能出现编码问题;
  • 解决方案:始终使用QString存储和传递文件路径,避免使用char*

坑 2:忘记关闭文件导致资源泄漏

  • 原因:QFile打开后未调用close(),导致文件句柄被占用,其他程序无法访问;
  • 解决方案:
    1. 养成 "打开 - 操作 - 关闭" 的习惯;
    2. 使用 RAII 思想(如自定义类封装QFile,在析构函数中调用close())。

坑 3:二进制文件使用文本模式打开导致损坏

  • 原因:文本模式会自动转换换行符(\n\r\n),破坏二进制文件结构;
  • 解决方案:二进制文件读写时,不添加QIODevice::Text模式。

坑 4:readAll()读取大文件导致内存溢出

  • 原因:一次性读取超大文件的所有数据,超出内存限制;
  • 解决方案:使用readLine()read(maxSize)分块读取,处理后立即释放内存。

坑 5:跨平台路径分隔符问题

  • 原因:Windows 使用\,Linux/macOS 使用/,手动拼接路径会导致跨平台兼容性问题;
  • 解决方案:使用QDir::separator()获取当前系统的路径分隔符,或直接使用/(Qt 会自动转换)。

总结

掌握 Qt 文件操作,能让你在处理配置文件、日志记录、数据导入导出等场景时游刃有余。建议结合 Qt 助手(Qt Assistant)深入学习QFileQFileInfoQIODevice等类的详细 API,多动手实践不同场景的文件操作,才能真正做到灵活运用。

如果你有任何问题或需要进一步探讨高级场景,欢迎在评论区留言交流!

相关推荐
秃了也弱了。17 小时前
FASTJSON库:阿里出品java界json解析库,使用与踩坑记录
java·开发语言·json
CSDN_RTKLIB17 小时前
【std::map】双向迭代器说明
c++·stl
王老师青少年编程18 小时前
信奥赛C++提高组csp-s之欧拉回路
c++·算法·csp·欧拉回路·信奥赛·csp-s·提高组
No0d1es18 小时前
2025年12月 GESP CCF编程能力等级认证C++六级真题
c++·青少年编程·gesp·ccf·6级
Terrence Shen18 小时前
【CUDA编程系列】之01
c++·人工智能·深度学习·机器学习
墨有66618 小时前
数学分析栈的出栈顺序:从算法判断到数学本质(卡特兰数初探)
c++·算法·数学建模
superman超哥18 小时前
Rust 或模式(Or Patterns)的语法:多重匹配的优雅表达
开发语言·后端·rust·编程语言·rust或模式·or patterns·多重匹配
SmartRadio18 小时前
MK8000(UWB射频芯片)与DW1000的协议适配
c语言·开发语言·stm32·单片机·嵌入式硬件·物联网·dw1000
guygg8818 小时前
基于捷联惯导与多普勒计程仪组合导航的MATLAB算法实现
开发语言·算法·matlab