跨平台文件名和 wxString::ToStdString()

Cross-platform file names and wxString::ToStdString()

我想使用 wxWidgets 3.1 在跨平台应用程序中处理文件。我依赖一些只接受文件名作为 std::string.

的函数

在 Windows 上,我可以简单地使用 wxString::ToStdString(),一切都很好。

在 Linux(Ubuntu 20.04 LTS)上,如果文件名或路径中有“特殊”字符(例如,法语的默认下载目录 Ubuntu "Téléchargement").

当我在 Linux 上明确指定以下转换器时,转换成功:std::string str = wxs.ToStdString(wxMBConvUTF8());

但这不适用于 Windows 并且会打乱“特殊”字符。

我想,我可以编写依赖于平台的代码来处理这个问题,但这违背了工具包的目的。

我对此做了很多研究,但现在我完全糊涂了。我认为 wxString 在 Unicode wxWidgets 构建(我正在使用)中使用 std::string?为什么这(显然)依赖于平台?我错过了什么?

这是一个最小的例子,会弹出三个消息框:第一个正确显示wxString,第二个不显示字符串(因为转换失败),第三个显示字符串一次转换是显式完成的。在 Windows 上,前两个框正确显示字符串,最后一个框显示两个 'é' 的错误字符。

#include "wx/wx.h"
#include <fstream>

class MyApp : public wxApp
{
public:
    virtual bool OnInit() wxOVERRIDE;
};

class MyFrame : public wxFrame
{
public:
    MyFrame(const wxString& title);

private:
};
wxIMPLEMENT_APP(MyApp);


bool MyApp::OnInit()
{
    if (!wxApp::OnInit())
        return false;

    // create the main application window
    MyFrame* frame = new MyFrame("Minimal wxWidgets App");
    frame->Show(true);
    return true;
}


// Some file I/O function
std::string openFile(std::string fileName)
{
    std::ifstream file(fileName);
    if (file)
    {
        return "Success!";
    }
    else
    {
        return "Failure!";
    }

}

// frame constructor
MyFrame::MyFrame(const wxString& title)
    : wxFrame(NULL, wxID_ANY, title)
{
    wxString name = wxFileSelector("Pick a file");  // Pick a file with a "special" character in the name, e.g. Äréa.txt

    wxMessageBox(openFile(name.ToStdString()));  // Success! on Windows; Failure! on Linux

    wxMessageBox(openFile(std::string(name.ToUTF8())));  // Failure! on Windows; Success! on Linux
}

编辑: 我发现最近添加到 wxWidgets(在 3.1.5 中):

Add wxString::utf8_string() This adds a yet another conversion function, which is not ideal, but still better than having to write ToStdString(wxConvUTF8) every time for losslessly converting wxString to std::string: not only this is too long, but it's also too easy to forget to specify wxConvUTF8, resulting in data loss when using non-UTF-8 locale.

所以你会认为这一切都清楚了。但是我 使用 UTF-8 语言环境!这是 locale 命令的输出:

LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC=de_DE.UTF-8
LC_TIME=de_DE.UTF-8
LC_COLLATE="en_US.UTF-8"
LC_MONETARY=de_DE.UTF-8
LC_MESSAGES="en_US.UTF-8"
LC_PAPER=de_DE.UTF-8
LC_NAME=de_DE.UTF-8
LC_ADDRESS=de_DE.UTF-8
LC_TELEPHONE=de_DE.UTF-8
LC_MEASUREMENT=de_DE.UTF-8
LC_IDENTIFICATION=de_DE.UTF-8
LC_ALL=

EDIT2:我将最小示例更改为更接近我实际尝试做的事情。

EDIT3:我将示例更改为 even 更接近我实际尝试做的事情。 :-)

您应该使用 wxString::fn_str(),其中 returns 适合用作文件名的适合类型的字符串。

如果您在 Windows 上使用 wxString::ToStdString(),则一切都不再“正常”,例如字符串中的西里尔字母并且您的语言环境是德语。转换失败。

显然函数 wxString::ToStdString() 使用程序当前语言环境的编码。

程序的默认语言环境不是用户环境中设置的语言环境。所有 C 和 C++ 程序都在名为“C”的语言环境中启动。为了使用用户环境中指定的语言环境,需要调用

setlocale(LC_ALL, "");

在节目开头。 C++ 有自己的方法

 std::locale::global(std::locale(""));

但有时不行,估计是C++标准库实现有bug,所以C方式更靠谱

如果您的框架有自己的语言环境处理方法,您可能应该改用它。

“接受文件名为 std::string 的函数”必须以 UTF-8 格式接受它们,因此您需要使用 wxString::utf8_str() 或其同义词 ToUTF8() 而不是依赖于当前像你目前所做的那样进行语言环境编码。在任何情况下,您仍然需要将结果传递给 std::string ctor,因为这些函数 return char* 指针或缓冲区。

wxWidgets最新的3.1版本还有wxString::utf8_string(),直接return是UTF-8编码的std::string,使用起来更方便。