获取boost :: locale :: conv

时间:2016-06-29 18:49:58

标签: c++ windows utf-8 codepages boost-locale

手头的任务

我在Windows上从UTF-8编码的XML解析文件名。我需要将该文件名传递给我无法改变的函数。在内部,它使用_fsopen(),它不支持Unicode字符串。

目前的方法

我目前的做法是将文件名转换为用户的字符集,希望文件名可以在该编码中表示。我然后使用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 localeWindows-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,系统语言为德语,用户语言为英语

2 个答案:

答案 0 :(得分:2)

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(来自您提供的链接)以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;
}

答案 1 :(得分:2)

巴马克的方式是最好的方式。

要清除语言环境,该过程始终以“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");