Domanda Stumped con Unicode, Boost, C ++, codecvts


In C ++, voglio usare Unicode per fare cose. Così, dopo essere caduto nella tana del coniglio di Unicode, sono riuscito a finire in un treno in disordine, confusione, mal di testa e locali.

Ma in Boost ho avuto lo sfortunato problema di provare a utilizzare i percorsi di file Unicode e provare a utilizzare la libreria di opzioni del programma Boost con l'input Unicode. Ho letto tutto ciò che ho trovato sugli argomenti di locales, codecvts, codifiche Unicode e Boost.

Il mio attuale tentativo di far funzionare le cose è di avere un codecvt che prende una stringa UTF-8 e la converte nella codifica della piattaforma (UTF-8 su POSIX, UTF-16 su Windows), ho cercato di evitare wchar_t.

Il più vicino che ho effettivamente ottenuto sta cercando di farlo con Boost.Locale, per convertire da una stringa UTF-8 a una stringa UTF-32 in uscita.

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

Penso di aver avuto un altro tipo di conversione che utilizzava caratteri ampi, ma davvero non so cosa sto facendo. Non so quale sia lo strumento giusto per il lavoro a questo punto. Aiuto?


12
2017-10-22 12:49


origine


risposte:


Ok, dopo alcuni mesi ho capito, e mi piacerebbe aiutare le persone in futuro.

Prima di tutto, la cosa codectt era il modo sbagliato di farlo. Boost.Locale offre un modo semplice per convertire i set di caratteri nel suo spazio dei nomi boost :: locale :: conv. Ecco un esempio (ce ne sono altri non basati su locales).

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

Come puoi vedere, se sostituisci "en_US.utf-32" con "" verrà generato nelle impostazioni locali dell'utente.

Non so ancora come rendere std :: cout farlo sempre, ma la funzione translate () di Boost.Locale viene riprodotta nelle impostazioni locali dell'utente.

Per quanto riguarda il filesystem usando le stringhe UTF-8 multipiattaforma, sembra che sia possibile, ecco un link su come farlo.


11
2017-12-09 06:56



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

Questo non fa alcuna conversione, dal momento che usa codecvt<char, char, mbstate_t> che è un no-op. Gli unici flussi standard che utilizzano codecvt sono file-stream. std :: cout non è richiesto per eseguire alcuna conversione.

Per forzare Boost.Filesystem ad interpretare stringhe strette come UTF-8 su Windows, utilizzare boost::filesystem::imbue con una locale con un facet di codecvt UTF-8 UTF-16. Boost.Locale ha un'implementazione di quest'ultimo.


3
2017-10-22 13:06



Le classi di sostituzione iostream del file system Boost funzionano bene con UTF-16 quando utilizzato con Visual C ++.

Tuttavia, non funzionano (nel senso di supportare nomi di file arbitrari) se utilizzati con g ++ in Windows - almeno a partire da Boost versione 1.47. C'è un commento in codice che lo spiega; in sostanza, la libreria standard di Visual C ++ fornisce non standard wchar_t costruttori basati su Boost che usano le classi del filesystem, ma g ++ non supporta queste estensioni.

Una soluzione alternativa è usare 8.3 nomi di file brevi, ma questa soluzione è un po 'fragile dato che con le vecchie versioni di Windows l'utente può disattivare la generazione automatica di nomi di file brevi.


Esempio di codice per l'utilizzo del filesystem Boost in 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;
}

2
2017-10-22 13:22