2009-02-05 11 views
5

Tengo un archivo que contiene cadenas UNICODE-16 que me gustaría leer en un programa Linux. Las cadenas se escribieron sin procesar desde el formato WCHAR interno de Windows. (¿Windows siempre usa UTF-16? Por ejemplo, en versiones japonesas)¿Cómo leo cadenas Unicode-16 desde un archivo utilizando métodos POSIX en Linux?

Creo que puedo leerlos usando lecturas sin formato y la conversión con wcstombs_l. Sin embargo, no puedo imaginarme qué locale usar. Ejecutar "locale -a" en mis máquinas actualizadas Ubuntu y Mac OS X produce cero configuraciones regionales con utf-16 en sus nombres.

¿Hay una manera mejor?

Actualización: la respuesta correcta y otras a continuación me ayudaron a utilizar libiconv. Aquí hay una función que estoy usando para hacer la conversión. Actualmente lo tengo dentro de una clase que hace que las conversiones se conviertan en una sola línea de código.

// Function for converting wchar_t* to char*. (Really: UTF-16LE --> UTF-8) 
// It will allocate the space needed for dest. The caller is 
// responsible for freeing the memory. 
static int iwcstombs_alloc(char **dest, const wchar_t *src) 
{ 
    iconv_t cd; 
    const char from[] = "UTF-16LE"; 
    const char to[] = "UTF-8"; 

    cd = iconv_open(to, from); 
    if (cd == (iconv_t)-1) 
    { 
    printf("iconv_open(\"%s\", \"%s\") failed: %s\n", 
      to, from, strerror(errno)); 
    return(-1); 
    } 

    // How much space do we need? 
    // Guess that we need the same amount of space as used by src. 
    // TODO: There should be a while loop around this whole process 
    //  that detects insufficient memory space and reallocates 
    //  more space. 
    int len = sizeof(wchar_t) * (wcslen(src) + 1); 

    //printf("len = %d\n", len); 

    // Allocate space 
    int destLen = len * sizeof(char); 
    *dest = (char *)malloc(destLen); 
    if (*dest == NULL) 
    { 
    iconv_close(cd); 
    return -1; 
    } 

    // Convert 

    size_t inBufBytesLeft = len; 
    char *inBuf = (char *)src; 
    size_t outBufBytesLeft = destLen; 
    char *outBuf = (char *)*dest; 

    int rc = iconv(cd, 
       &inBuf, 
       &inBufBytesLeft, 
       &outBuf, 
       &outBufBytesLeft); 
    if (rc == -1) 
    { 
    printf("iconv() failed: %s\n", strerror(errno)); 
    iconv_close(cd); 
    free(*dest); 
    *dest = NULL; 
    return -1; 
    } 

    iconv_close(cd); 

    return 0; 
} // iwcstombs_alloc() 

Respuesta

4

(ocurre con las ventanas siempre usar UTF-16? Por ejemplo, en las versiones en japonés)

Sí, WCHAR de NT es siempre la codificación UTF-16LE.

(La 'página de códigos del sistema', que para las instalaciones japonesas es de hecho cp932/Shift-JIS, todavía existe en NT para el beneficio de muchas, muchas aplicaciones que no son nativas de Unicode, rutas FAT32, etc. .)

Sin embargo, no se garantiza que wchar_t sea de 16 bits y en Linux no será así, se utilizará UTF-32 (UCS-4). Entonces wcstombs_l es poco probable que sea feliz.

Lo correcto sería utilizar una biblioteca como iconv para leerla en cualquier formato que esté utilizando internamente, presumiblemente wchar_t. Usted podría tratar de hackear usted mismo introduciendo bytes, pero es probable que obtenga cosas como los sustitutos incorrectos.

Runing "locale -a" en mis máquinas actualizadas de Ubuntu y Mac OS X produce cero configuraciones regionales con utf-16 en sus nombres.

De hecho, Linux no puede usar UTF-16 como codificación predeterminada de configuración regional gracias a todos los \ 0s.

+0

WCHAR en Windows parece tener un tamaño fijo (puede hacer sizeof() en él). ¿Eso no significa que solo implementa un subconjunto de UTF-16, que es de tamaño variable? – PolyThinker

+1

Almacena los valores de 16 bits correspondientes a los puntos de código UTF-16; si quieres personajes fuera del BMP tienes que usar los sustitutos manualmente, Windows no te ayudará. p.ej. '' .length == 2. Esta es la misma situación que, por ejemplo. Java o Python en modo estrecho-Unicode. – bobince

+0

Después de muchos experimentos y utilizando el conocimiento de esta respuesta, utilicé libiconv. Estoy agregando la función simple que utilicé aquí para que otros la usen. No es perfecto y animo a otros a solucionar los problemas. – Harvey

1

Recomiendo encarecidamente utilizar una codificación Unicode como representación interna de su programa. Use UTF-16 o UTF-8. Si usa UTF-16 internamente, entonces obviamente no se requiere traducción. Si usa UTF-8, puede usar una configuración regional con .UTF-8, como en_US.UTF-8.

+0

No tuve muchas opciones al principio ya que mi jefe fue quien escribió el código roto. Desde entonces, lo ayudé a ver las cosas de manera diferente y ahora utilizaremos UTF-8 para todos los datos almacenados. – Harvey

6

forma más sencilla es convertir el archivo de utf16 a la codificación UTF8 UNIX nativo y luego leerlo,

iconv -f utf16 -t utf8 file_in.txt -o file_out.txt 

También puede utilizar iconv (3) (ver man iconv 3) para convertir cadena usando C. La mayoría de otros idiomas tiene enlaces a iconv también.

Que puede utilizar cualquier configuración regional UTF-8 como en_US.UTF-8 que es generalmente la predeterminada en la mayoría de las distribuciones de Linux.

+0

No sabía acerca de esta herramienta. Esto no responde a mi pregunta b/c. Necesito leer/escribir los archivos de forma programática, pero conocer esta herramienta hace más fácil la generación de casos de prueba. Gracias. – Harvey

+0

La versión de inconv en mi sistema FreeBSD quería 'UTF-16' y' UTF-8' en lugar de 'utf16' o' utf8'. –

+0

Ver sin guardar: 'iconf -f utf16 -t utf8 SUFILE | menos' – Luc

2

Usted puede leer como binarios, a continuación, hacer su propia conversión rápida: http://unicode.org/faq/utf_bom.html#utf16-3 pero probablemente es más seguro utilizar una biblioteca (como libiconv) que se ocupa de las secuencias no válidas correctamente.

+0

Gracias por la pista. Mi jefe estaba usando esas funciones que apuntabas, pero cambiamos a libiconv ya que hace que sea fácil manejar diferentes conjuntos de codificación. – Harvey

Cuestiones relacionadas