UTF8 para / de ampla conversão de caracteres em STL

É possível converter uma string UTF8 em um std :: string para std :: wstring e vice-versa de maneira 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 à biblioteca C ++ padrão.

Eu fiz esta pergunta há 5 anos. Este tópico foi muito útil para mim naquela época, cheguei a uma conclusão, depois mudei para o meu projeto. É engraçado que eu precisei de algo similar recentemente, totalmente não relacionado a esse projeto do passado. Como eu estava pesquisando por possíveis soluções, me deparei com a minha própria pergunta 🙂

A solução que escolhi agora é baseada no C ++ 11. As bibliotecas de reforço que Constantin menciona em sua resposta agora fazem parte do padrão. Se replacemos std :: wstring pelo novo tipo de string std :: u16string, as conversões ficarão assim:

UTF-8 para UTF-16

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

UTF-16 para UTF-8

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

Como visto nas outras respostas, existem várias abordagens para o problema. É por isso que evito escolher uma resposta aceita.

UTF8-CPP: UTF-8 com C ++ em um modo portátil

Você pode extrair utf8_codecvt_facet da biblioteca de serialização do Boost .

Seu exemplo de uso:

  typedef wchar_t ucs4_t; std::locale old_locale; std::locale utf8_locale(old_locale,new utf8_codecvt_facet); // 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(ofs)); } // Read the UTF-8 data back in, converting to UCS-4 on the way in std::vector from_file; { std::wifstream ifs("data.ucd"); ifs.imbue(utf8_locale); ucs4_t item = 0; while (ifs >> item) from_file.push_back(item); } 

Procure pelos arquivos utf8_codecvt_facet.cpp e utf8_codecvt_facet.cpp em fonts de reforço.

A definição do problema afirma explicitamente que a codificação de caracteres de 8 bits é UTF-8. Isso torna isso um problema trivial; tudo o que é necessário é um pouco mais ou menos para converter de uma especificação UTF para outra.

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

O princípio é simples – passe pela input e monte um ponto de código Unicode de 32 bits de acordo com uma especificação UTF, em seguida, emita o ponto de código de acordo com as outras especificações. 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 faz deste um problema simples.

Aqui está uma rápida implementação do wchar_t para a conversão UTF-8 e vice-versa. Assume-se que a input já está devidamente codificada – o antigo ditado “Garbage in, garbage out” se aplica aqui. Acredito que verificar a codificação é feito melhor 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(codepoint)); else if (codepoint < = 0x7ff) { out.append(1, static_cast(0xc0 | ((codepoint >> 6) & 0x1f))); out.append(1, static_cast(0x80 | (codepoint & 0x3f))); } else if (codepoint < = 0xffff) { out.append(1, static_cast(0xe0 | ((codepoint >> 12) & 0x0f))); out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); out.append(1, static_cast(0x80 | (codepoint & 0x3f))); } else { out.append(1, static_cast(0xf0 | ((codepoint >> 18) & 0x07))); out.append(1, static_cast(0x80 | ((codepoint >> 12) & 0x3f))); out.append(1, static_cast(0x80 | ((codepoint >> 6) & 0x3f))); out.append(1, static_cast(0x80 | (codepoint & 0x3f))); } codepoint = 0; } } return out; } 

O código acima funciona para inputs UTF-16 e UTF-32, simplesmente porque o intervalo de d800 a dfff são pontos de código inválidos; eles indicam que você está decodificando o UTF-16. Se você sabe que wchar_t é de 32 bits, então você pode remover algum código para otimizar a function.

 std::wstring UTF8_to_wchar(const char * in) { std::wstring out; unsigned int codepoint; while (*in != 0) { unsigned char ch = static_cast(*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(codepoint)); else if (codepoint > 0xffff) { out.append(1, static_cast(0xd800 + (codepoint >> 10))); out.append(1, static_cast(0xdc00 + (codepoint & 0x03ff))); } else if (codepoint < 0xd800 || codepoint >= 0xe000) out.append(1, static_cast(codepoint)); } } return out; } 

Novamente, se você sabe que wchar_t tem 32 bits, você pode remover algum código dessa function, mas, nesse caso, isso não deve fazer nenhuma diferença. A expressão sizeof(wchar_t) > 2 é conhecida em tempo de compilation, portanto, qualquer compilador decente reconhecerá código morto e o removerá.

Existem várias maneiras de fazer isso, mas os resultados dependem do que as codificações de caracteres estão nas variables string e wstring .

Se você sabe que a string é ASCII, você pode simplesmente usar o construtor iterator do wstring :

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

Se sua string tiver alguma outra codificação, no entanto, você obterá resultados muito ruins. Se a codificação for Unicode, você poderá dar uma olhada no projeto ICU , que fornece um conjunto de plataformas cruzadas de bibliotecas que convertem para e de todos os tipos de codificações Unicode.

Se sua string contiver caracteres em uma página de código, então $ DEITY poderá ter piedade de sua alma.

ConvertUTF.h ConvertUTF.c

Crédito para bames53 por fornecer versões atualizadas

Você pode usar a faceta de codecvt idioma codecvt . Existe uma especialização específica definida, codecvt que pode ser útil para você, embora o comportamento seja específico do sistema e não garanta a conversão para UTF-8 de nenhuma maneira.

UTFConverter – confira esta biblioteca. Ele faz essa conversão, mas você também precisa da class ConvertUTF – eu encontrei aqui

Eu não acho que haja uma maneira portátil de fazer isso. C ++ não sabe a codificação de seus caracteres multibyte.

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