UTF-8 para / de conversão de char ampla em STL

votos
66

É possível converter string de UTF8 em um std :: string para std :: wstring e vice-versa de forma independente de plataforma? Em um aplicativo do Windows eu usaria MultiByteToWideChar e WideCharToMultiByte. No entanto, o código é compilado para vários sistemas operacionais e estou limitado a biblioteca padrão C ++.

Publicado 29/09/2008 em 13:03
fonte usuário
Em outras línguas...                            


10 respostas

votos
2

Você pode usar a codecvtfaceta local . Há uma especialização específica definida, codecvt<wchar_t, char, mbstate_t>que pode ser de utilidade para você, embora, o comportamento de que é específico do sistema, e não garante a conversão para UTF-8 em qualquer forma.

Respondeu 29/09/2008 em 13:07
fonte usuário

votos
-1

Eu não acho que há uma maneira portátil de fazer isso. C ++ não saber a codificação dos seus caracteres de vários bytes.

Como Chris sugeriu, sua melhor aposta é a de jogar com codecvt.

Respondeu 29/09/2008 em 13:16
fonte usuário

votos
23

Você pode extrair utf8_codecvt_facetda biblioteca de impulso serialização .

Seu exemplo de uso:

  typedef wchar_t ucs4_t;

  std::locale old_locale;
  std::locale utf8_locale(old_locale,new utf8_codecvt_facet<ucs4_t>);

  // Set a New global locale
  std::locale::global(utf8_locale);

  // Send the UCS-4 data out, converting to UTF-8
  {
    std::wofstream ofs("data.ucd");
    ofs.imbue(utf8_locale);
    std::copy(ucs4_data.begin(),ucs4_data.end(),
          std::ostream_iterator<ucs4_t,ucs4_t>(ofs));
  }

  // Read the UTF-8 data back in, converting to UCS-4 on the way in
  std::vector<ucs4_t> from_file;
  {
    std::wifstream ifs("data.ucd");
    ifs.imbue(utf8_locale);
    ucs4_t item = 0;
    while (ifs >> item) from_file.push_back(item);
  }

Procure utf8_codecvt_facet.hppe utf8_codecvt_facet.cpparquivos em fontes impulso.

Respondeu 29/09/2008 em 14:36
fonte usuário

votos
13

Existem várias maneiras de fazer isso, mas os resultados dependem do que as codificações de caracteres estão nas stringe wstringvariáveis.

Se você souber o stringé ASCII, você pode simplesmente usar wstring's construtor iterator:

string s = "This is surely ASCII.";
wstring w(s.begin(), s.end());

Se o seu stringtem alguma outra codificação, no entanto, você vai obter resultados muito ruins. Se a codificação é Unicode, você poderia dar uma olhada no projeto de UTI , que fornece um conjunto multi-plataforma de bibliotecas que convertem de e para todos os tipos de codificações Unicode.

Se o seu stringcontém caracteres em uma página de código, em seguida, pode $ DIVINDADE tenha piedade de sua alma.

Respondeu 29/09/2008 em 14:44
fonte usuário

votos
15

A definição do problema afirma explicitamente que a codificação de caracteres de 8 bits é UTF-8. Isso torna este um problema trivial; tudo o que exige é um pouco-twiddling para converter de uma especificação UTF para outro.

Basta olhar para as codificações sobre estas páginas da Wikipedia para UTF-8 , UTF-16 e UTF-32 .

O princípio é simples - ir através da entrada e montar um ponto de código Unicode de 32 bits de acordo com uma especificação UTF, em seguida, emite o código de ponto de acordo com outra especificação. Os pontos de código individuais não precisam de tradução, como seria necessário com qualquer outra codificação de caracteres; isso é o que torna este um problema simples.

Aqui está uma rápida implementação wchar_tda conversão UTF-8 e vice-versa. Assume-se que a entrada já está devidamente codificado - o velho "Garbage in, garbage out" ditado se aplica aqui. Eu acredito que a verificação da codificação é o melhor feito como uma etapa separada.

std::string wchar_to_UTF8(const wchar_t * in)
{
    std::string out;
    unsigned int codepoint = 0;
    for (in;  *in != 0;  ++in)
    {
        if (*in >= 0xd800 && *in <= 0xdbff)
            codepoint = ((*in - 0xd800) << 10) + 0x10000;
        else
        {
            if (*in >= 0xdc00 && *in <= 0xdfff)
                codepoint |= *in - 0xdc00;
            else
                codepoint = *in;

            if (codepoint <= 0x7f)
                out.append(1, static_cast<char>(codepoint));
            else if (codepoint <= 0x7ff)
            {
                out.append(1, static_cast<char>(0xc0 | ((codepoint >> 6) & 0x1f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else if (codepoint <= 0xffff)
            {
                out.append(1, static_cast<char>(0xe0 | ((codepoint >> 12) & 0x0f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            else
            {
                out.append(1, static_cast<char>(0xf0 | ((codepoint >> 18) & 0x07)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 12) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | ((codepoint >> 6) & 0x3f)));
                out.append(1, static_cast<char>(0x80 | (codepoint & 0x3f)));
            }
            codepoint = 0;
        }
    }
    return out;
}

O código acima funciona tanto para entrada UTF-16 e UTF-32, simplesmente porque a gama d800através dfffsão pontos de código inválidos; eles indicam que você está decodificação UTF-16. Se você sabe que wchar_té de 32 bits, então você pode remover algum código para otimizar a função.

std::wstring UTF8_to_wchar(const char * in)
{
    std::wstring out;
    unsigned int codepoint;
    while (*in != 0)
    {
        unsigned char ch = static_cast<unsigned char>(*in);
        if (ch <= 0x7f)
            codepoint = ch;
        else if (ch <= 0xbf)
            codepoint = (codepoint << 6) | (ch & 0x3f);
        else if (ch <= 0xdf)
            codepoint = ch & 0x1f;
        else if (ch <= 0xef)
            codepoint = ch & 0x0f;
        else
            codepoint = ch & 0x07;
        ++in;
        if (((*in & 0xc0) != 0x80) && (codepoint <= 0x10ffff))
        {
            if (sizeof(wchar_t) > 2)
                out.append(1, static_cast<wchar_t>(codepoint));
            else if (codepoint > 0xffff)
            {
                out.append(1, static_cast<wchar_t>(0xd800 + (codepoint >> 10)));
                out.append(1, static_cast<wchar_t>(0xdc00 + (codepoint & 0x03ff)));
            }
            else if (codepoint < 0xd800 || codepoint >= 0xe000)
                out.append(1, static_cast<wchar_t>(codepoint));
        }
    }
    return out;
}

Novamente, se você sabe que wchar_té de 32 bits você pode remover algum código desta função, mas, neste caso, não deve fazer qualquer diferença. A expressão sizeof(wchar_t) > 2é conhecido em tempo de compilação, portanto, qualquer compilador decente vai reconhecer código morto e removê-lo.

Respondeu 29/09/2008 em 15:00
fonte usuário

Respondeu 29/09/2008 em 15:42
fonte usuário

votos
4

ConvertUTF.h ConvertUTF.c

Crédito para bames53 para fornecer versões atualizadas

Respondeu 28/09/2010 em 19:10
fonte usuário

votos
1

UTFConverter - confira esta biblioteca. Ele faz tal conversão, mas você precisa também de classe ConvertUTF - Eu achei aqui

Respondeu 09/09/2011 em 09:19
fonte usuário

votos
39

Eu fiz esta pergunta há 5 anos. Esta discussão foi muito útil para mim naquela época, cheguei a uma conclusão, então eu segui em frente com o meu projeto. É engraçado que eu precisava de algo semelhante recentemente, totalmente alheios a esse projeto do passado. Como eu estava pesquisando para possíveis soluções, me deparei com a minha própria pergunta :)

A solução que escolheu agora é baseada em C ++ 11. As bibliotecas de impulso que Constantin menciona em sua resposta agora fazem parte da norma. Se substituirmos std :: wstring com o novo tipo de cadeia std :: u16string, então as conversões será parecido com este:

UTF-8 para UTF-16

std::string source;
...
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::u16string dest = convert.from_bytes(source);    

UTF-16 para UTF-8

std::u16string source;
...
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>,char16_t> convert;
std::string dest = convert.to_bytes(source);    

Como visto das outras respostas, existem várias abordagens para o problema. É por isso que eu abster-se de escolher uma resposta aceita.

Respondeu 11/02/2013 em 10:47
fonte usuário

votos
0

Criado minha própria biblioteca para utf-8 para utf-16 / utf-32 conversão -, mas decidiu fazer um fork do projeto existente para o efeito.

https://github.com/tapika/cutf

(Originada a partir de https://github.com/noct/cutf )

API trabalha com C simples, bem como com o C ++.

Protótipos de função se parece com isso: (Para uma lista completa ver https://github.com/tapika/cutf/blob/master/cutf.h )

//
//  Converts utf-8 string to wide version.
//
//  returns target string length.
//
size_t utf8towchar(const char* s, size_t inSize, wchar_t* out, size_t bufSize);

//
//  Converts wide string to utf-8 string.
//
//  returns filled buffer length (not string length)
//
size_t wchartoutf8(const wchar_t* s, size_t inSize, char* out, size_t outsize);

#ifdef __cplusplus

std::wstring utf8towide(const char* s);
std::wstring utf8towide(const std::string& s);
std::string  widetoutf8(const wchar_t* ws);
std::string  widetoutf8(const std::wstring& ws);

#endif

Exemplo de uso / aplicação de teste para o teste de conversão UTF:

#include "cutf.h"

#define ok(statement)                                       \
    if( !(statement) )                                      \
    {                                                       \
        printf("Failed statement: %s\n", #statement);       \
        r = 1;                                              \
    }

int simpleStringTest()
{
    const wchar_t* chineseText = L"主体";
    auto s = widetoutf8(chineseText);
    size_t r = 0;

    printf("simple string test:  ");

    ok( s.length() == 6 );
    uint8_t utf8_array[] = { 0xE4, 0xB8, 0xBB, 0xE4, 0xBD, 0x93 };

    for(int i = 0; i < 6; i++)
        ok(((uint8_t)s[i]) == utf8_array[i]);

    auto ws = utf8towide(s);
    ok(ws.length() == 2);
    ok(ws == chineseText);

    if( r == 0 )
        printf("ok.\n");

    return (int)r;
}
Respondeu 02/06/2019 em 16:09
fonte usuário

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more