Поставлен в тупик с Unicode, Boost, C ++, codecvts - PullRequest
12 голосов
/ 22 октября 2011

В C ++ я хочу использовать Unicode для работы. Так что, упав в кроличью нору Unicode, мне удалось оказаться в железнодорожной катастрофе, вызванной растерянностью, головными болями и местами.

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

Моя текущая попытка заставить все работать, - это иметь codecvt, который принимает строку 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;
}

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

Ответы [ 3 ]

11 голосов
/ 09 декабря 2011

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

Во-первых, кодеквт был неправильным способом сделать это.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 кроссплатформенную строку, кажется, что это возможно, вот ссылка на то, как это сделать .

3 голосов
/ 22 октября 2011
  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.Boost.Locale имеет реализацию последнего.

2 голосов
/ 22 октября 2011

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

Однако они не работают (в смысле поддержки произвольных имен файлов) при использовании с g ++ в Windows - по крайней мере, в версии Boost 1.47. Есть комментарий к коду, объясняющий это; по сути, стандартная библиотека Visual C ++ предоставляет нестандартные конструкторы 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;
}
...