QT 中文问题和路径中空格问题

记录在使用 QT 开发过程中碰到的关于 QSetting 编写配置文件读写中文的问题,QProcess command 路径中存在中文的问题,以及使用 QString 转 std::string 时,原QString携带中文的问题

QProcess::startDetached() 或者 start() command路径中空格问题

在开发过程中遇到一个重启客户端的问题失败的问题

错误示例:

QString filepath = qApp->applicationFilePath();
QProcess::startDetached(filepath);

当路径中没有空格是没有问题,但是当程序安装在 ‪D:\Program Files 路径下,这一段代码就会失效

更正

QString infilepath = qApp->applicationFilePath();
infilepath = infilepath.replace("/","\\");
QString filepath = "\"" + infilepath + "\"";
QProcess::startDetached(filepath);

为了找到空格具体影响的原因,需要看一下 QProcess::startDetached(const QString &command) 这个函数的源码

bool QProcess::startDetached(const QString &command)
{
    QStringList args = parseCombinedArgString(command);
    if (args.isEmpty())
        return false;

    const QString prog = args.takeFirst();

    return QProcessPrivate::startDetached(prog, args);
}

从这里可以看出,解析 command 可能出现问题, 因为一个 cmd 命名可以分为2部分,一部分为 prog 应用程序部分,另一部分是 args 参数,所以接下来主要是看 static QStringList parseCombinedArgString(const QString &program) 这个函数

static QStringList parseCombinedArgString(const QString &program)
{
    QStringList args;
    QString tmp;
    int quoteCount = 0;
    bool inQuote = false;

    // handle quoting. tokens can be surrounded by double quotes
    // "hello world". three consecutive double quotes represent
    // the quote character itself.
    for (int i = 0; i < program.size(); ++i) {
        if (program.at(i) == QLatin1Char('"')) {
            ++quoteCount;
            if (quoteCount == 3) {
                // third consecutive quote
                quoteCount = 0;
                tmp += program.at(i);
            }
            continue;
        }
        if (quoteCount) {
            if (quoteCount == 1)
                inQuote = !inQuote;
            quoteCount = 0;
        }
        if (!inQuote && program.at(i).isSpace()) {
            if (!tmp.isEmpty()) {
                args += tmp;
                tmp.clear();
            }
        } else {
            tmp += program.at(i);
        }
    }
    if (!tmp.isEmpty())
        args += tmp;

    return args;
}

从这一段代码可以看出,先判断 " 双引号,双引号不会存入tmp,(当三个双引号连续,会写入tmp 一个), 并且以 program.at(i).isSpace() 空格来判断 args 的结尾

按照上面代码的规则
QString filepath = "D:\\Program Files\\1.exe"; 路径为例
最后解析的结果为 D:\ProgramFiles\1.exe 2部分

所以这里有个很简单的解决方案,就是改成 QString filepath = "\"D:\\Program Files\\1.exe\"";

如果携带参数的话 可以用空格将所有参数隔开, QString filepath = "\"D:\\Program Files\\1.exe\" -help";

关于路径中 “\” 和 “/” 的差异我的理解是:

  • Windows下使用的是反斜杠 “\”,
  • Linux下使用的是正斜杠 “/”

但是在QT里,我用获取路径的函数等得到的 QString 路径用的都是 “/” 正斜杠,可能是因为 “\” 反斜杠是转义字符,所以得到的路径我一般

  • 先使用replace(“/”,”\\”)转一下路径中的正斜杠
  • 然后用双引号包一下就可以解决这个问题了

    QString infilepath = qApp->applicationFilePath(); infilepath = infilepath.replace(“/”,”\”); QString filepath = “\“” + infilepath + “\“”; QProcess::startDetached(filepath);

最后总结一下

static bool startDetached(const QString &command);
void start(const QString &command, OpenMode mode = ReadWrite);

在使用这2个需要解析指令和参数的函数时,需要注意指令部分需要使用双引号包起来

QSetting 读写中文的问题

读取和设置的时候都需要设置编码格式,settings.setIniCodec(QTextCodec::codecForName("GB2312"));

// 设置
void setValueForSettings(const QString &target, const QVariant &value)
{
    QString strFileName = QCoreApplication::applicationDirPath() + "/config.ini" ;
    QSettings settings(strFileName, QSettings::IniFormat);
    settings.setIniCodec(QTextCodec::codecForName("GB2312"));
    settings.setValue(target, value);
}

// 读取
QVariant getValueFromSettings(const QString &target)
{
    QString strFileName = QCoreApplication::applicationDirPath() +"/config.ini" ;
    QSettings settings(strFileName, QSettings::IniFormat);
    settings.setIniCodec(QTextCodec::codecForName("GB2312"));
    QVariant value = settings.value(target);
    return value;
}

中文路径问题

在使用一些 C 的函数时例如 std::fstream 的 open 时,直接使用 QT 的 toStdString() 函数将 QString 类型转成 std::string 时,只要路径中存在中文,转换出来的新的 std::string 实际上是乱码, C 函数并不能正确使用,这主要是因为 toStdString() 函数本身调用的是 Utf8 的编码, 所以想正确转换, 需要使用 QTextCodec 类,声明转码器为 GBK 或者 GB2312

#include <QTextCodec>

QTextCodec *code = QTextCodec::codecForName("GB2312");//解决中文路径问题
std::string name = code->fromUnicode(dir).data();