2012-06-22 19 views
7

Mi software está obteniendo algunas cadenas en UTF8 de lo que necesito para convertir a ISO 8859 1. Sé que el dominio UTF8 es más grande que iso 8859. Pero los datos en UTF8 se han convertido previamente desde ISO, por lo que no debería perderse nada.¿Hay alguna manera de convertir de UTF8 a iso-8859-1?

Me gustaría saber si hay una manera fácil/directa de convertir de UTF8 a iso-8859-1.

Gracias

+1

Si está utilizando una biblioteca que realizó la conversión, también debería tener algo para convertirla de nuevo. Asumiendo que no cambiaste ningún carácter en la cuerda, deberías estar bien devolviéndolo. – RedX

Respuesta

11

Aquí es una función que puede resultar útil: utf8_to_latin9(). Se convierte en ISO-8859-15 (incluido EURO, que no tiene ISO-8859-1), pero también funciona correctamente para la parte de conversión UTF-8 ->ISO-8859-1 de un ISO-8859-1 ->UTF-8 ->ISO-8859-1 ida y vuelta.

La función ignora los puntos de código no válidos similares a //IGNORE para iconv, pero no recompone las secuencias descompuestas UTF-8; es decir, no convertirá U+006E U+0303 en U+00F1. No me molesto en recomponerme porque iconv tampoco lo hace.

La función es muy cuidadosa con el acceso a la cadena. Nunca escaneará más allá del buffer. El almacenamiento intermedio de salida debe ser un byte más largo que la longitud, ya que siempre agrega el byte NUL al final de la cadena. La función devuelve el número de caracteres (bytes) en la salida, sin incluir el byte NUL al final de la cadena.

/* UTF-8 to ISO-8859-1/ISO-8859-15 mapper. 
* Return 0..255 for valid ISO-8859-15 code points, 256 otherwise. 
*/ 
static inline unsigned int to_latin9(const unsigned int code) 
{ 
    /* Code points 0 to U+00FF are the same in both. */ 
    if (code < 256U) 
     return code; 
    switch (code) { 
    case 0x0152U: return 188U; /* U+0152 = 0xBC: OE ligature */ 
    case 0x0153U: return 189U; /* U+0153 = 0xBD: oe ligature */ 
    case 0x0160U: return 166U; /* U+0160 = 0xA6: S with caron */ 
    case 0x0161U: return 168U; /* U+0161 = 0xA8: s with caron */ 
    case 0x0178U: return 190U; /* U+0178 = 0xBE: Y with diaresis */ 
    case 0x017DU: return 180U; /* U+017D = 0xB4: Z with caron */ 
    case 0x017EU: return 184U; /* U+017E = 0xB8: z with caron */ 
    case 0x20ACU: return 164U; /* U+20AC = 0xA4: Euro */ 
    default:  return 256U; 
    } 
} 

/* Convert an UTF-8 string to ISO-8859-15. 
* All invalid sequences are ignored. 
* Note: output == input is allowed, 
* but input < output < input + length 
* is not. 
* Output has to have room for (length+1) chars, including the trailing NUL byte. 
*/ 
size_t utf8_to_latin9(char *const output, const char *const input, const size_t length) 
{ 
    unsigned char    *out = (unsigned char *)output; 
    const unsigned char  *in = (const unsigned char *)input; 
    const unsigned char *const end = (const unsigned char *)input + length; 
    unsigned int    c; 

    while (in < end) 
     if (*in < 128) 
      *(out++) = *(in++); /* Valid codepoint */ 
     else 
     if (*in < 192) 
      in++;    /* 10000000 .. 10111111 are invalid */ 
     else 
     if (*in < 224) {  /* 110xxxxx 10xxxxxx */ 
      if (in + 1 >= end) 
       break; 
      if ((in[1] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x1FU)) << 6U) 
          | ((unsigned int)(in[1] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 2; 

     } else 
     if (*in < 240) {  /* 1110xxxx 10xxxxxx 10xxxxxx */ 
      if (in + 2 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x0FU)) << 12U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[2] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 3; 

     } else 
     if (*in < 248) {  /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 3 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x07U)) << 18U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[3] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 4; 

     } else 
     if (*in < 252) {  /* 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 4 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U && 
       (in[4] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x03U)) << 24U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 18U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[3] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[4] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 5; 

     } else 
     if (*in < 254) {  /* 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ 
      if (in + 5 >= end) 
       break; 
      if ((in[1] & 192U) == 128U && 
       (in[2] & 192U) == 128U && 
       (in[3] & 192U) == 128U && 
       (in[4] & 192U) == 128U && 
       (in[5] & 192U) == 128U) { 
       c = to_latin9((((unsigned int)(in[0] & 0x01U)) << 30U) 
          | (((unsigned int)(in[1] & 0x3FU)) << 24U) 
          | (((unsigned int)(in[2] & 0x3FU)) << 18U) 
          | (((unsigned int)(in[3] & 0x3FU)) << 12U) 
          | (((unsigned int)(in[4] & 0x3FU)) << 6U) 
          | ((unsigned int)(in[5] & 0x3FU))); 
       if (c < 256) 
        *(out++) = c; 
      } 
      in += 6; 

     } else 
      in++;    /* 11111110 and 11111111 are invalid */ 

    /* Terminate the output string. */ 
    *out = '\0'; 

    return (size_t)(out - (unsigned char *)output); 
} 

Tenga en cuenta que puede agregar transliteración a medida para puntos de código específico en la función to_latin9(), sino que se limitan a las sustituciones de un solo carácter.

Como está escrito actualmente, la función puede realizar conversiones in situ de forma segura: los punteros de entrada y salida pueden ser los mismos. La cadena de salida nunca será más larga que la cadena de entrada. Si su cadena de entrada tiene espacio para un byte extra (por ejemplo, tiene el NUL que termina la cadena), puede usar la función anterior para convertirla de UTF-8 a ISO-8859-1/15. Deliberadamente lo escribí de esta manera, porque debería ahorrarte un poco de esfuerzo en un entorno integrado, aunque este enfoque es un poco limitado. personalización y extensión.

Editar:

He incluido un par de funciones de conversión de in an edit to this answer tanto para América-1/9 a/desde UTF-8 conversión (ISO-8859-1 o -15 a/desde UTF-8); la principal diferencia es que esas funciones devuelven una copia asignada dinámicamente y mantienen la cadena original intacta.

10

iconv - realizar la conversión de juego de caracteres

size_t iconv(iconv_t cd, char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);

iconv_t iconv_open(const char *tocode, const char *fromcode);

tocode es "ISO_8859-1" y fromcode es "UTF-8".

Ejemplo de trabajo:

#include <iconv.h> 
#include <stdio.h> 

int main (void) { 
    iconv_t cd = iconv_open("ISO_8859-1", "UTF-8"); 
    if (cd == (iconv_t) -1) { 
     perror("iconv_open failed!"); 
     return 1; 
    } 

    char input[] = "Test äöü"; 
    char *in_buf = &input[0]; 
    size_t in_left = sizeof(input) - 1; 

    char output[32]; 
    char *out_buf = &output[0]; 
    size_t out_left = sizeof(output) - 1; 

    do { 
     if (iconv(cd, &in_buf, &in_left, &out_buf, &out_left) == (size_t) -1) { 
      perror("iconv failed!"); 
      return 1; 
     } 
    } while (in_left > 0 && out_left > 0); 
    *out_buf = 0; 

    iconv_close(cd); 

    printf("%s -> %s\n", input, output); 
    return 0; 
} 
+0

Gracias, el problema principal que tengo y olvidé especificar es que mi software se ejecuta en Linux incorporado e iconv no está disponible. – fazineroso

+0

Puede compilar iconv para su Linux. ¿Tu linux usa glibc? En caso afirmativo, tiene su implementación compatible llamada 'gconv': http://www.gnu.org/software/libc/manual/html_node/glibc-iconv-Implementation.html –

+0

@fazineroso hay soluciones que no emplean la biblioteca llamadas. Tengo que irme ahora, pero actualizaré mi respuesta si no obtienes una mejor para mañana más o menos. – kay

Cuestiones relacionadas