Получить имя кодовой страницы пользователя для функций в boost :: locale :: conv

Задача под рукой

Я анализирую имя файла из XML-кода в кодировке UTF-8 в Windows. Мне нужно передать это имя в функцию, которую я не могу изменить. Внутренне он использует _fsopen() который не поддерживает строки Unicode.

Текущий подход

Мой текущий подход заключается в том, чтобы преобразовать имя файла в кодировку пользователя, надеясь, что имя файла представимо в этой кодировке. Я тогда использую boost::locale::conv::from_utf() конвертировать из UTF-8 и я использую boost::locale::util::get_system_locale() чтобы получить название текущей локали.

Жизнь хороша?

Я на немецкой системе с использованием кодовой страницы Windows-1252 таким образом get_system_locale() правильно дает de_DE.windows-1252. Если я проверяю подход с именем файла, содержащим умлаут, все работает как положено.

Эта проблема

Просто чтобы убедиться, что я переключил мою систему локали на украинский, который использует кодовую страницу 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 хотя якобы меняю его как локального админа? Точно так же язык системы — немецкий, хотя язык моего аккаунта — английский. (Вход в систему немецкий, пока я не войду)

  • я должен придерживаться используя короткие имена файлов? Кажется, не работает надежно, хотя.

Хорошая печать

  • Компилятор MSVC18
  • Boost — версия 1.56.0, бэкэнд предположительно winapi
  • Система Win7, язык системы немецкий, язык пользователя английский

2

Решение

ANSI устарела, так что не беспокойтесь об этом.

Windows использует UTF16, вы должны конвертировать из UTF8 в UTF16, используя MultiByteToWideChar, Это преобразование безопасно.

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;
}

Редактировать, получая ANSI от UTF8, используя GetACP()

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;
}
2

Другие решения

Путь Бармака — лучший способ сделать это.

Чтобы разобраться с языковым стандартом, процесс всегда начинается с языкового стандарта «C». Вы можете использовать функция setlocale установить языковой стандарт по умолчанию или любой произвольный языковой стандарт.

#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");
2