"C .UTF-8" Язык C ++ на Windows? - PullRequest
3 голосов
/ 09 января 2020

Я нахожусь в процессе исправления большого кроссплатформенного приложения с открытым исходным кодом, чтобы оно могло обрабатывать пути к файлам, содержащие не-ANSI символы в Windows.


Обновление:

Основываясь на полученных мной ответах и ​​комментариях (спасибо!), Я чувствую, что должен уточнить некоторые моменты:

  1. Я не могу изменить код десятков сторонних библиотек для использования std::wchar_t. Это просто не вариант. Решение должно работать с обычными std::fopen(), std::ifstream, et c.

  2. Решение, которое я обрисовал в общих чертах ниже, работает на 99%, , по крайней мере, на система, которую я разрабатываю на (Windows 10 версия 1909, сборка 18363.535). Я еще не тестировал ни в одной другой системе.

    Единственная оставшаяся проблема, , по крайней мере, в моей системе , это в основном форматирование чисел, и я надеюсь, что замена фасета std::numpunct делает трюк (но я пока не преуспел).


Мое текущее решение включает в себя:

  1. Установка C языковой стандарт .UTF-8 для категории LC_CTYPE в Windows (для всех остальных категорий устанавливается языковой стандарт C в соответствии с требованиями приложения):

    // Required by the application.
    std::setlocale(LC_ALL, "C");
    
    // On Windows, we want std::fopen() and other functions dealing with strings
    // and file paths to accept narrow-character strings encoded in UTF-8.
    #ifdef _WIN32
    {
    #ifndef NDEBUG
        char* new_ctype_locale =
    #endif
            std::setlocale(LC_CTYPE, ".UTF-8");
        assert(new_ctype_locale != nullptr);
    }
    #endif
    
  2. Настройка boost::filesystem::path для использования языкового стандарта en_US.UTF-8, чтобы он также мог работать с путями, содержащими символы, отличные от ANSI:

    boost::filesystem::path::imbue(std::locale("en_US.UTF-8"));
    

Последний отсутствующий бит - это исправление файлового ввода-вывода использование потоков C ++, таких как

std::ifstream istream(filename);

Самое простое решение, вероятно, состоит в том, чтобы установить глобальный языковой стандарт C ++ в начале приложения:

std::locale::global(std::locale("en_US.UTF-8"));

Однако это приводит к неправильному форматированию чисел, например 1234,56 форматируется как 1234,56.

Существует ли локаль, которая просто определяет кодировку UTF-8, не мешая с форматированием чисел (или другими вещами)?

В основном я ищу язык C.UTF-8, но, похоже, его нет на Windows.

Обновление: я полагаю, что одним из решений будет сброс некоторых (большинство? все?) аспектов локали, но мне трудно найти информацию о том, как это сделать.

Ответы [ 2 ]

1 голос
/ 09 января 2020

Windows API не учитывает локали CRT, а реализация CRT fopen et c. напрямую вызывайте API-интерфейс small-char, поэтому изменение языкового стандарта не повлияет на кодировку.

Однако в обновлении Windows 10 мая 2019 г. (версия 1903) введена поддержка UTF-8 в его узком API-интерфейсы * char . Это можно включить, вставив соответствующий манифест в ваш исполняемый файл. К сожалению, это очень недавнее добавление, и поэтому может не подойти, если вам нужно настроить таргетинг на более старые системы.

В число других вариантов входит ручное преобразование в wchar_t или использование слоя, который делает это за вас (например, Boost). .Файл системы или, что еще лучше, Boost.Nowide ).

1 голос
/ 09 января 2020

Не берите в голову локали.

На Windows вы должны использовать расширение Microsoft , которое добавляет конструктор, принимающий const std::wchar_t* (ожидается, что он будет указывать на UTF-16) к std::ifstream.

Надеемся, что все ваши строки - это UTF-8, или, иначе, какая-то согласованная и нормальная кодировка.

Так что просто возьмите UTF-8 → UTF-16 конвертер (они легковесны) и передайте имена файлов на std::ifstream как UTF-16 (в std::wchar_t*).

(Обязательно #ifdef, чтобы не было попыток на любой другой платформе.)

Вы должны также использовать _wfopen вместо std::fopen, таким же образом, по той же причине.

Вот и все.

...