2012-10-12 460 views
5

La pregunta que tengo es bastante simple, pero no pude encontrar una solución hasta el momento:Cómo convertir una cadena de UTF8 a Latin1 en C/C++?

¿Cómo puedo convertir un UTF8 codificado string a un latin1 codificado string en C++ sin utilizar cualquier librería adicionales como libiconv?

Cada ejemplo que pude encontrar hasta ahora es para la conversión de latin1 a UTF8?

+2

UTF8 puede representar 65.536 puntos de código; latin1 (ISO-8859-1) solo puede representar 256. ¿Cómo desea tratar con todos los caracteres que no se pueden convertir? – simonc

+0

Puede traducir a C este http://www.jamesmurty.com/2011/12/30/python-code-utf8-to-latin1/ (tenga en cuenta que no todos los símbolos se pueden convertir) –

+1

condición @DavidRF "sin usar cualquier libs extra "significa no usar funciones preparadas como en la última línea del código dado,' utf8_text.encode ('ISO-8859-1', 'reemplazar') ' – Dialecticus

Respuesta

4
typedef unsigned value_type; 

template <typename Iterator> 
size_t get_length (Iterator p) 
{ 
    unsigned char c = static_cast<unsigned char> (*p); 
    if (c < 0x80) return 1; 
    else if (!(c & 0x20)) return 2; 
    else if (!(c & 0x10)) return 3; 
    else if (!(c & 0x08)) return 4; 
    else if (!(c & 0x04)) return 5; 
    else return 6; 
} 

template <typename Iterator> 
value_type get_value (Iterator p) 
{ 
    size_t len = get_length (p); 

    if (len == 1) 
    return *p; 

    value_type res = static_cast<unsigned char> (
            *p & (0xff >> (len + 1))) 
            << ((len - 1) * 6); 

    for (--len; len; --len) 
     res |= (static_cast<unsigned char> (*(++p)) - 0x80) << ((len - 1) * 6); 

    return res; 
} 

Esta función devolverá el punto de código Unicode al p. Ahora puede convertir una cadena usando

for (std::string::iterator p = s_utf8.begin(); p != s_utf8.end(); ++p) 
{ 
    value_type value = get_value<std::string::iterator&>(p)); 
    if (value > 0xff) 
     throw "AAAAAH!"; 
    s_latin1.append(static_cast<char>(value)); 
} 

No hay garantías, el código es bastante antiguo :)

+0

Y sí, sé que UTF-8 oficialmente solo admite una longitud máxima de 4 bytes, esto se puede implementar de forma más pedante. – filmor

+0

¿Esto también convierte las diéresis (ö, ä, ß) correctamente? – ashiaka

+0

@ashiaka: lo dudo ... No creo que esos personajes estén disponibles en latin1 ... – Goz

-2

latin1 (alias ISO-8859-1) define los primeros 256 puntos de código de Unicode. Por lo tanto, en UTF-8, si su personaje es de 8 bits, entonces se asignará exactamente al equivalente latin1. Si tiene más de 8 bits de longitud, no hay corresponsal dentro de latin1 y debe asignarlo a un "carácter desconocido" (por ejemplo, \0 o?).

+3

Esto no es verdad. Solo funciona así para * 7 * bits. – filmor

+0

¿De verdad? Maldición ... En cuyo caso, supongo que el OP puede usar esto y luego mapear manualmente los 128 puntos restantes. – Xophmeister

+0

La conversión de UTF-16 a latin1 simplemente elimina cada cero par, pero la conversión de UTF-8 a latin1 es un poco complicada. – Dialecticus

1

Aquí es una versión de la respuesta de filmor que escribí para mis propósitos. Un poco más legible, probablemente un poco más lento. No necesitaba las plantillas, ya que siempre me ocupé del char *, y en mi caso quería reemplazar caracteres no latinos con _. Sólo en caso de que ayuda a alguien:

int GetUtf8CharacterLength(unsigned char utf8Char) 
{ 
    if (utf8Char < 0x80) return 1; 
    else if ((utf8Char & 0x20) == 0) return 2; 
    else if ((utf8Char & 0x10) == 0) return 3; 
    else if ((utf8Char & 0x08) == 0) return 4; 
    else if ((utf8Char & 0x04) == 0) return 5; 

    return 6; 
} 

char Utf8ToLatin1Character(char *s, int *readIndex) 
{ 
    int len = GetUtf8CharacterLength(static_cast<unsigned char>(s[ *readIndex ])); 
    if (len == 1) 
    { 
     char c = s[ *readIndex ]; 
     (*readIndex)++; 

     return c; 
    } 

    unsigned int v = (s[ *readIndex ] & (0xff >> (len + 1))) << ((len - 1) * 6); 
    (*readIndex)++; 
    for (len-- ; len > 0 ; len--) 
    { 
     v |= (static_cast<unsigned char>(s[ *readIndex ]) - 0x80) << ((len - 1) * 6); 
     (*readIndex)++; 
    } 

    return (v > 0xff) ? 0 : (char)v; 
} 

// overwrites s in place 
char *Utf8ToLatin1String(char *s) 
{ 
    for (int readIndex = 0, writeIndex = 0 ; ; writeIndex++) 
    { 
     if (s[ readIndex ] == 0) 
     { 
      s[ writeIndex ] = 0; 
      break; 
     } 

     char c = Utf8ToLatin1Character(s, &readIndex); 
     if (c == 0) 
     { 
      c = '_'; 
     } 

     s[ writeIndex ] = c; 
    } 

    return s; 
} 

Código de ensayo:

char s2[ 256 ] = "lif\xc3\xa9 is b\xc3\xa9tt\xc3\xa9r with acc\xc3\xa9nts"; 
Utf8ToLatin1String(s2); 
Cuestiones relacionadas