Подтвердить что ты не робот

Нагрузка с Unicode, Boost, С++, кодеки

В С++ я хочу использовать Unicode для работы. Поэтому, после падения кроличьей дыры в Юникоде, мне удалось закончить крушение поезда с путаницей, головными болями и локалями.

Но в Boost у меня была неудачная проблема с попыткой использования путей к файлам Unicode и попыткой использовать библиотеку опций программы Boost с вводом Unicode. Я читал все, что мог найти по темам локалей, кодеков, кодировок Unicode и Boost.

Моя текущая попытка заставить работать, это иметь кодек, который берет строку UTF-8 и преобразует ее в кодировку платформы (UTF-8 на POSIX, UTF-16 в Windows), я пытался избегайте wchar_t.

Самый близкий, который я действительно получил, пытается сделать это с помощью Boost.Locale, чтобы преобразовать из строки UTF-8 в строку UTF-32 на выходе.

#include <string>
#include <boost/locale.hpp>
#include <locale>

int main(void)
{
  std::string data("Testing, 㤹");

  std::locale fromLoc = boost::locale::generator().generate("en_US.UTF-8");
  std::locale toLoc   = boost::locale::generator().generate("en_US.UTF-32");

  typedef std::codecvt<wchar_t, char, mbstate_t> cvtType;
  cvtType const* toCvt = &std::use_facet<cvtType>(toLoc);

  std::locale convLoc = std::locale(fromLoc, toCvt);

  std::cout.imbue(convLoc);
  std::cout << data << std::endl;

  // Output is unconverted -- what?

  return 0;
}

Я думаю, что у меня было какое-то другое преобразование, использующее широкие символы, но я действительно не знаю, что я делаю. Я не знаю, каков правильный инструмент для работы на этом этапе. Помощь?

4b9b3361

Ответ 1

Хорошо, через несколько месяцев я понял это, и я хотел бы помочь людям в будущем.

Прежде всего, проблема с кодеком была неправильным способом. Boost.Locale предоставляет простой способ преобразования наборов символов в пространство boost:: locale:: conv. Вот один пример (другие не основаны на локалях).

#include <boost/locale.hpp>
namespace loc = boost::locale;

int main(void)
{
  loc::generator gen;
  std::locale blah = gen.generate("en_US.utf-32");

  std::string UTF8String = "Tésting!";
  // from_utf will also work with wide strings as it uses the character size
  // to detect the encoding.
  std::string converted = loc::conv::from_utf(UTF8String, blah);

  // Outputs a UTF-32 string.
  std::cout << converted << std::endl;

  return 0;
}

Как вы можете видеть, если вы замените "en_US.utf-32" на "", он будет выводиться в пользовательской локали.

Я до сих пор не знаю, как заставить std:: cout делать это все время, но функция translate() для выходов Boost.Locale в пользовательской локали.

Что касается файловой системы, использующей кросс-платформу UTF-8, кажется, что это возможно, здесь ссылка на то, как для этого.

Ответ 2

  std::cout.imbue(convLoc);
  std::cout << data << std::endl;

Это не делает преобразование, , так как использует codecvt<char, char, mbstate_t>, который не работает. Единственными стандартными потоками, которые используют codecvt, являются файловые потоки. std:: cout не требуется вообще выполнять преобразование.

Чтобы заставить Boost.Filesystem интерпретировать узкие строки как UTF-8 на окнах, используйте boost::filesystem::imbue с языковой версией с UTF- 8 ↔ UTF-16 codecvt facet. Boost.Locale имеет реализацию последнего.

Ответ 3

Классы замены iostream для файловой системы Boost отлично работают с UTF-16 при использовании с Visual С++.

Однако они не работают (в смысле поддержки произвольных имен файлов) при использовании с g++ в Windows - по крайней мере, с версии Boost 1.47. Это комментарий к коду, объясняющий это; по существу, стандартная библиотека Visual С++ предоставляет нестандартные конструкторы на основе wchar_t, которые используют классы файловой системы Boost, но g++ не поддерживает эти расширения.

Обходным путем является использование 8.3 коротких имен файлов, но это решение немного хрупкое, поскольку со старыми версиями Windows пользователь может отключить автоматическое создание коротких имен файлов.


Пример кода для использования файловой системы Boost в Windows:
#include "CmdLineArgs.h"        // CmdLineArgs
#include "throwx.h"             // throwX, hopefully
#include "string_conversions.h" // ansiOrFillerFrom( wstring )

#include <boost/filesystem/fstream.hpp>     // boost::filesystem::ifstream
#include <iostream>             // std::cout, std::cerr, std::endl
#include <stdexcept>            // std::runtime_error, std::exception
#include <string>               // std::string
#include <stdlib.h>             // EXIT_SUCCESS, EXIT_FAILURE
using namespace std;
namespace bfs = boost::filesystem;

inline string ansi( wstring const& ws ) { return ansiWithFillersFrom( ws ); }

int main()
{
    try
    {
        CmdLineArgs const   args;
        wstring const       programPath     = args.at( 0 );

        hopefully( args.nArgs() == 2 )
            || throwX( "Usage: " + ansi( programPath ) + " FILENAME" );

        wstring const       filePath        = args.at( 1 );
        bfs::ifstream       stream( filePath );     // Nice Boost ifstream subclass.
        hopefully( !stream.fail() )
            || throwX( "Failed to open file '" + ansi( filePath ) + "'" );

        string line;
        while( getline( stream, line ) )
        {
            cout << line << endl;
        }
        hopefully( stream.eof() )
            || throwX( "Failed to list contents of file '" + ansi( filePath ) + "'" );

        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}