Manejo de caracteres multibyte (no ASCII) en C

Estoy intentando hacer mi propia versión de wc (filtro de Unix), pero tengo un problema con los caracteres que no son ASCII. Hice un volcado HEX de un archivo de texto y descubrí que estos caracteres ocupan más de un byte, por lo que no se ajustan a char. ¿Hay alguna manera en que pueda leer estos caracteres del archivo y manejarlos como un solo carácter (para poder contar los caracteres en el archivo) en C? He estado buscando en Google un poco y encontré algún tipo wchar_t, pero no había ningún ejemplo simple de cómo usarlo con archivos.

He estado buscando en Google un poco y encontré algún tipo wchar_t, pero no había ningún ejemplo simple de cómo usarlo con archivos.

Bien atendidas. No hubo ejemplos simples porque, desafortunadamente, la compatibilidad con el conjunto de caracteres adecuado no es simple .

Aparte: en un mundo ideal, todo el mundo usaría UTF-8 (una encoding Unicode que es eficiente en memoria, robusta y compatible con versiones anteriores con ASCII), la biblioteca C estándar incluiría el soporte de deencoding y encoding UTF-8, y la respuesta a esta pregunta (y tratar con el texto en general) sería simple y directo.

La respuesta a la pregunta ” ¿Cuál es la mejor biblioteca de Unicode para C? ” Es utilizar la biblioteca de ICU . Es posible que desee consultar ustdio.h , ya que tiene una función u_fgetc , y agregar soporte Unicode a su progtwig probablemente tome poco más que escribir u_ unas cuantas veces.

Además, si puede dedicar unos minutos a una lectura ligera, puede leer The Absolute Minimum Todos los desarrolladores de software deben saber absolutamente, positivamente sobre Unicode y los conjuntos de caracteres (¡sin excusas!) De Joel On Software.

Yo, personalmente, nunca he usado UCI, pero probablemente lo haré de ahora en adelante 🙂

Si desea escribir una versión C estándar de la utilidad wc que respete la configuración de idioma actual cuando se ejecuta, puede utilizar las versiones wchar_t de las funciones de stdio. Al inicio del progtwig, debe llamar a setlocale() :

 setlocale(LC_CTYPE, ""); 

Esto hará que las funciones de caracteres anchos utilicen el conjunto de caracteres apropiado definido por el entorno, por ejemplo, En sistemas similares a Unix, la variable de entorno LANG . Por ejemplo, esto significa que si su variable LANG se establece en una configuración regional UTF8 , las funciones de caracteres anchos manejarán la entrada y salida en UTF8. (Así es como se especifica la utilidad POSIX wc ).

A continuación, puede utilizar las versiones de caracteres anchos de todas las funciones estándar. Por ejemplo, si tienes código como este:

 long words = 0; int in_word = 0; int c; while ((c = getchar()) != EOF) { if (isspace(c)) { if (in_word) { in_word = 0; words++; } } else { in_word = 1; } } 

… lo convertiría a la versión de caracteres anchos cambiando c a wint_t , getchar() a getwchar() , EOF a WEOF y isspace() a iswspace() :

 long words = 0; int in_word = 0; wint_t c; while ((c = getwchar()) != WEOF) { if (iswspace(c)) { if (in_word) { in_word = 0; words++; } } else { in_word = 1; } } 

Ve a echar un vistazo a la UCI . Esa biblioteca es lo que necesitas para lidiar con todos los problemas.

La mayoría de las respuestas hasta ahora tienen mérito, pero las que utiliza dependen de la semántica que desee:

  • Si desea procesar texto en la encoding de la configuración regional configurada, y no le importa el error completo en el caso de encontrar secuencias no válidas, usar getwchar() está bien.
  • Si desea procesar texto en la encoding de la configuración regional configurada, pero necesita detectar y recuperarse de secuencias no válidas, necesita leer bytes y usar mbrtowc manualmente.
  • Si siempre desea procesar texto como UTF-8, necesita leer bytes y enviarlos a su propio decodificador. Si sabe de antemano que el archivo será UTF-8 válido, solo puede contar bytes en los rangos 00-7F y C2-F4 y omitir el conteo de todos los demás bytes, pero esto podría dar resultados incorrectos en la presencia de secuencias no válidas. Un enfoque más robusto sería decodificar el bytestream a puntos de código Unicode y contar el número de decodificaciones exitosas.

Espero que esto ayude.

¿Estás seguro de que realmente necesitas la cantidad de caracteres ? wc cuenta el número de bytes .

 ~$ echo 'דניאל' > hebrew.txt ~$ wc hebrew.txt 1 1 11 hebrew.txt 

(11 = 5 caracteres de dos bytes + 1 byte para ‘\ n’)

Sin embargo, si realmente desea contar caracteres en lugar de bytes, y puede asumir que sus archivos de texto están codificados en UTF-8, entonces el método más sencillo es contar todos los bytes que no son bytes de seguimiento (es decir, en el rango de 0x80 a 0xBF).

Si no puede asumir UTF-8 pero puede asumir que cualquier archivo que no sea UTF-8 está en una encoding de un solo byte, realice una verificación de validación de UTF-8 en los datos. Si pasa, devuelve el número de bytes de ventaja UTF-8. Si falla, devuelve el número total de bytes.

(Tenga en cuenta que el enfoque anterior es específico para wc . Si realmente está haciendo algo con los personajes en lugar de simplemente contarlos, deberá conocer la encoding).