获取 boost::locale::conv 中函数的用户代码页名称
Get the user's codepage name for functions in boost::locale::conv
手头的任务
我正在 Windows 上解析 UTF-8 编码 XML 的文件名。我需要将该文件名传递给一个我无法更改的函数。它在内部使用不支持 Unicode 字符串的 _fsopen()
。
当前方法
我目前的方法是将文件名转换为用户的字符集,希望文件名可以用该编码表示。然后我使用 boost::locale::conv::from_utf()
从 UTF-8 转换,我使用 boost::locale::util::get_system_locale()
获取当前语言环境的名称。
生活好不好?
我在使用代码页 Windows-1252 的德语系统上,因此 get_system_locale()
正确地产生 de_DE.windows-1252。如果我使用包含变音符号的文件名测试该方法,一切都会按预期工作。
问题
只是为了确保我 switched my system locale to Ukrainian which uses code page Windows-1251。在文件名中使用一些西里尔字母我的方法失败了。原因是 get_system_locale()
仍然产生 de_DE.windows-1252 现在是不正确的。
另一方面,GetACP()
正确地为德国语言环境产生 1252,为乌克兰语言环境产生 1251。我也知道 Boost.Locale 可以转换为给定的语言环境,因为这个小测试程序按我的预期工作:
#include <boost/locale.hpp>
#include <iostream>
#include <string>
#include <windows.h>
int main()
{
std::cout << "Codepage: " << GetACP() << std::endl;
std::cout << "Boost.Locale: " << boost::locale::util::get_system_locale() << std::endl;
namespace blc = boost::locale::conv;
// Cyrillic small letter zhe -> \xe6 (ш on 1251, æ on 1252)
std::string const test1251 = blc::from_utf(std::string("\xd0\xb6"), "windows-1251");
std::cout << "1251: " << static_cast<int>(test1251.front()) << std::endl;
// Latin small letter sharp s -> \xdf (Я on 1251, ß on 1252)
auto const test1252 = blc::from_utf(std::string("\xc3\x9f"), "windows-1252");
std::cout << "1252: " << static_cast<int>(test1252.front()) << std::endl;
}
问题
如何以 Boost.Locale 支持的格式查询用户区域设置的名称?使用 std::locale("").name()
会产生 German_Germany.1252,使用它会导致 boost::locale::conv::invalid_charset_error
异常。
系统区域设置是否可能保持 de_DE.windows-1252 尽管我应该以本地管理员身份更改它?同样,系统语言是德语,但我帐户的语言是英语。 (在我登录之前,登录屏幕是德语)
我应该坚持使用 using short filenames 吗?不过似乎工作不可靠。
细则
- 编译器是 MSVC18
- Boost 是 1.56.0 版本,后端应该是 winapi
- 系统为Win7,系统语言为德语,用户语言为英语
ANSI 已被弃用,所以不要为它烦恼。
Windows 使用 UTF16,您必须使用 MultiByteToWideChar
从 UTF8 转换为 UTF16。这种转换是安全的。
std::wstring getU16(const std::string &str)
{
if (str.empty()) return std::wstring();
int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &res[0], sz);
return res;
}
然后您使用 _wfsopen
(来自您提供的 link)打开具有 UTF16 名称的文件。
int main()
{
//UTF8 source:
std::string filename_u8;
//This line works in VS2015 only
//For older version comment out the next line, obtain UTF8 from another source
filename_u8 = u8"c:\test\__ελληνικά.txt";
//convert to UTF16
std::wstring filename_utf16 = getU16(filename_u8);
FILE *file = NULL;
_wfopen_s(&file, filename_utf16.c_str(), L"w");
if (file)
{
//Add BOM, optional...
//Write the file name in to file, for testing...
fwrite(filename_u8.data(), 1, filename_u8.length(), file);
fclose(file);
}
else
{
cout << "access denined, or folder doesn't exits...
}
return 0;
}
编辑,使用 GetACP()
从 UTF8 获取 ANSI
std::wstring string_to_wstring(const std::string &str, int codepage)
{
if (str.empty()) return std::wstring();
int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
return res;
}
std::string wstring_to_string(const std::wstring &wstr, int codepage)
{
if (wstr.empty()) return std::string();
int sz = WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), 0, 0, 0, 0);
std::string res(sz, 0);
WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), &res[0], sz, 0, 0);
return res;
}
std::string get_ansi_from_utf8(const std::string &utf8, int codepage)
{
std::wstring utf16 = string_to_wstring(utf8, CP_UTF8);
std::string ansi = wstring_to_string(utf16, codepage);
return ansi;
}
Barmak 的方法是最好的方法。
要清理区域设置,该过程始终从 "C" 区域设置开始。您可以使用 setlocale function 将语言环境设置为系统默认值或任意语言环境。
#include <clocale>
// Get the current locale
setlocale(LC_ALL,NULL);
// Set locale to system default
setlocale(LC_ALL,"");
// Set locale to German
setlocale(LC_ALL,"de-DE");
手头的任务
我正在 Windows 上解析 UTF-8 编码 XML 的文件名。我需要将该文件名传递给一个我无法更改的函数。它在内部使用不支持 Unicode 字符串的 _fsopen()
。
当前方法
我目前的方法是将文件名转换为用户的字符集,希望文件名可以用该编码表示。然后我使用 boost::locale::conv::from_utf()
从 UTF-8 转换,我使用 boost::locale::util::get_system_locale()
获取当前语言环境的名称。
生活好不好?
我在使用代码页 Windows-1252 的德语系统上,因此 get_system_locale()
正确地产生 de_DE.windows-1252。如果我使用包含变音符号的文件名测试该方法,一切都会按预期工作。
问题
只是为了确保我 switched my system locale to Ukrainian which uses code page Windows-1251。在文件名中使用一些西里尔字母我的方法失败了。原因是 get_system_locale()
仍然产生 de_DE.windows-1252 现在是不正确的。
另一方面,GetACP()
正确地为德国语言环境产生 1252,为乌克兰语言环境产生 1251。我也知道 Boost.Locale 可以转换为给定的语言环境,因为这个小测试程序按我的预期工作:
#include <boost/locale.hpp>
#include <iostream>
#include <string>
#include <windows.h>
int main()
{
std::cout << "Codepage: " << GetACP() << std::endl;
std::cout << "Boost.Locale: " << boost::locale::util::get_system_locale() << std::endl;
namespace blc = boost::locale::conv;
// Cyrillic small letter zhe -> \xe6 (ш on 1251, æ on 1252)
std::string const test1251 = blc::from_utf(std::string("\xd0\xb6"), "windows-1251");
std::cout << "1251: " << static_cast<int>(test1251.front()) << std::endl;
// Latin small letter sharp s -> \xdf (Я on 1251, ß on 1252)
auto const test1252 = blc::from_utf(std::string("\xc3\x9f"), "windows-1252");
std::cout << "1252: " << static_cast<int>(test1252.front()) << std::endl;
}
问题
如何以 Boost.Locale 支持的格式查询用户区域设置的名称?使用
std::locale("").name()
会产生 German_Germany.1252,使用它会导致boost::locale::conv::invalid_charset_error
异常。系统区域设置是否可能保持 de_DE.windows-1252 尽管我应该以本地管理员身份更改它?同样,系统语言是德语,但我帐户的语言是英语。 (在我登录之前,登录屏幕是德语)
我应该坚持使用 using short filenames 吗?不过似乎工作不可靠。
细则
- 编译器是 MSVC18
- Boost 是 1.56.0 版本,后端应该是 winapi
- 系统为Win7,系统语言为德语,用户语言为英语
ANSI 已被弃用,所以不要为它烦恼。
Windows 使用 UTF16,您必须使用 MultiByteToWideChar
从 UTF8 转换为 UTF16。这种转换是安全的。
std::wstring getU16(const std::string &str)
{
if (str.empty()) return std::wstring();
int sz = MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(CP_UTF8, 0, &str[0], (int)str.size(), &res[0], sz);
return res;
}
然后您使用 _wfsopen
(来自您提供的 link)打开具有 UTF16 名称的文件。
int main()
{
//UTF8 source:
std::string filename_u8;
//This line works in VS2015 only
//For older version comment out the next line, obtain UTF8 from another source
filename_u8 = u8"c:\test\__ελληνικά.txt";
//convert to UTF16
std::wstring filename_utf16 = getU16(filename_u8);
FILE *file = NULL;
_wfopen_s(&file, filename_utf16.c_str(), L"w");
if (file)
{
//Add BOM, optional...
//Write the file name in to file, for testing...
fwrite(filename_u8.data(), 1, filename_u8.length(), file);
fclose(file);
}
else
{
cout << "access denined, or folder doesn't exits...
}
return 0;
}
编辑,使用
GetACP()
从 UTF8 获取 ANSI
std::wstring string_to_wstring(const std::string &str, int codepage)
{
if (str.empty()) return std::wstring();
int sz = MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), 0, 0);
std::wstring res(sz, 0);
MultiByteToWideChar(codepage, 0, &str[0], (int)str.size(), &res[0], sz);
return res;
}
std::string wstring_to_string(const std::wstring &wstr, int codepage)
{
if (wstr.empty()) return std::string();
int sz = WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), 0, 0, 0, 0);
std::string res(sz, 0);
WideCharToMultiByte(codepage, 0, &wstr[0], (int)wstr.size(), &res[0], sz, 0, 0);
return res;
}
std::string get_ansi_from_utf8(const std::string &utf8, int codepage)
{
std::wstring utf16 = string_to_wstring(utf8, CP_UTF8);
std::string ansi = wstring_to_string(utf16, codepage);
return ansi;
}
Barmak 的方法是最好的方法。
要清理区域设置,该过程始终从 "C" 区域设置开始。您可以使用 setlocale function 将语言环境设置为系统默认值或任意语言环境。
#include <clocale>
// Get the current locale
setlocale(LC_ALL,NULL);
// Set locale to system default
setlocale(LC_ALL,"");
// Set locale to German
setlocale(LC_ALL,"de-DE");