跨平台文件名和 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
,使用起来更方便。
我想使用 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
,使用起来更方便。