Bueno, tengo muchas dudas, cómo funciona C con codificaciones, primero tengo un archivo C, guardado con codificación ISO 8859-1, con contenido test.c, al ejecutar el programa, el carácter ÿ no se muestra correctamente en la consola de linux, sé que por defecto usa utf-8, pero si utf-8 usa los mismos 256 caracteres que ISO 8859-1, ¿por qué el programa no muestra correctamente el carácter 'ÿ'? Otra pregunta, ¿por qué test2 muestra correctamente el carácter 'ÿ'? donde el archivo test2.c es un UTF-8 y también el archivo.txt es un UTF-8? En otras palabras, ¿no se quejó el compilador de que el ancho era de varios caracteres?
prueba1.c
// ISO 8859-1 #include <stdio.h> int main(void) { unsigned char c = 'ÿ'; putchar(c); return 0; } $ gcc -o test1 test1.c $ ./test1 $ ▒prueba2.c
// ASCII #include <stdio.h> int main(void) { FILE *fp = fopen("file.txt", "r+"); int c; while((c = fgetc(fp)) != EOF) putchar(c); return 0; }archivo.txt: UTF-8 abcdefÿghi
$ gcc -o test2 test2.c $ ./test2 $ abcdefÿghibueno eso es todo, si me pueden ayudar dando detalles al respecto se los agradeceria mucho, :)
Las codificaciones de caracteres pueden ser confusas por muchas razones. Aquí hay algunas explicaciones:
En la codificación ISO 8859-1, el carácter y con una diéresis ÿ (originalmente una ligadura de i y j ) se codifica como un valor de byte de 0xFF (255). Los primeros 256 puntos de código en Unicode corresponden a los mismos caracteres que los de ISO 8859-1, pero la popular codificación UTF-8 para Unicode utiliza 2 bytes para puntos de código mayores de 127, por lo que ÿ se codifica en UTF-8 como 0xC3 0xBF .
Cuando lee el archivo file.txt , su programa lee un byte a la vez y lo envía a la consola sin cambios (a excepción de los finales de línea en los sistemas heredados), el ÿ se lee como 2 bytes separados que se envían uno tras otro, y el terminal muestra ÿ porque la configuración regional seleccionada para el terminal también usa la codificación UTF-8.
Para aumentar la confusión, si el archivo de origen utiliza la codificación UTF-8, "ÿ" es una cadena de longitud 2 y 'ÿ' se analiza como una constante de caracteres multibyte. Las constantes de caracteres multibyte son muy confusas y no portátiles (el valor puede ser 0xC3BF o 0xBFC3 dependiendo del sistema), se desaconseja enfáticamente usarlas y el compilador debe configurarse para emitir una advertencia cuando vea una ( gcc -Wall -Wextra ).
Aún más confuso es esto: en muchos sistemas, el tipo char está firmado por defecto. En este caso, la constante de carácter 'ÿ' (un solo byte en ISO 8859-1) tiene un valor de -1 y escribe int , sin importar cómo lo escribas en el código fuente: '\377' y '\xff' también tendrá un valor de -1 . La razón de esto es la coherencia con el valor de "ÿ"[0] , un char con el valor -1 . Este es también el valor más común de la macro EOF .
En todos los sistemas, getchar() y funciones similares como getc() y fgetc() devuelven valores entre 0 y UCHAR_MAX o el valor negativo especial de EOF , por lo que el byte 0xFF de un archivo donde el carácter ÿ está codificado como ISO 8859-1 es devuelto como el valor 0xFF o 255 , que se compara diferente de 'ÿ' si el char está firmado, y también diferente de 'ÿ' si el código fuente está en UTF-8.
Como regla general, no use caracteres que no sean ASCII en constantes de caracteres, no haga suposiciones sobre la codificación de caracteres utilizada para cadenas y contenidos de archivos y configure el compilador para que char no esté firmado de manera predeterminada ( -funsigned-char ).
Si trabaja con idiomas extranjeros, se recomienda encarecidamente utilizar UTF-8 para todos los contenidos textuales, incluido el código fuente. Tenga en cuenta que los caracteres que no son ASCII se codifican como varios bytes con esta codificación. Estudie la codificación UTF-8 , es bastante simple y elegante, y use bibliotecas para manejar transformaciones textuales como mayúsculas.
El problema aquí es que el unsigned char representa un número entero sin signo de tamaño 8 bits (de 0 a 255). C usa valores ASCII para representar caracteres. Un carácter ASCII es simplemente un número entero de 0 a 127. Por ejemplo, A es 65.
Cuando usa 'A' , el compilador entiende 65 . Pero, 'ÿ' no es un carácter ASCII, es un carácter ASCII extendido (con un valor de 152). Técnicamente, puede caber dentro de un unsigned char , pero el estándar C requiere que la sintaxis '' contenga un carácter ASCII estándar.
Así que es por eso que el primer ejemplo no funcionó.
Ahora para el segundo. Un carácter que no sea ASCII no puede caber en un solo carácter. La forma en que puede manejar caracteres fuera del conjunto ASCII limitado es mediante el uso de varios caracteres. Cuando escribe ÿ en un archivo, en realidad está escribiendo una representación binaria de este carácter. Si está utilizando la representación UTF-8 , esto significa que en su archivo tiene dos números de 8 bits 0xC3 y 0xBF .
Cuando lea su archivo en el bucle while de test2.c , en algún momento, c tomará el valor 0xC3 y luego 0xBF en la siguiente iteración. Estos dos valores se le darán a putc . Y luego, cuando se muestren, los dos valores juntos se interpretarán como ÿ .
Cuando putc finalmente escribe los caracteres, eventualmente son leídos por su aplicación de terminal. Si es compatible UTF-8 , puede entender el significado de 0xC3 seguido de 0xBF y mostrar un ÿ .
Entonces, la razón por la que, en el primer ejemplo, no vio ÿ es que el valor de c en su código es en realidad (probablemente) 0xC3 , que no representa ningún carácter.
Un ejemplo más concreto:
#include <stdio.h> int main() { char y[3] = { 0xC3, 0xBF, '\0' }; printf("%s\n", y); } Esto mostrará ÿ pero, como puede ver, se necesitan 2 caracteres para hacerlo.
si utf-8 usa los mismos 256 caracteres que ISO 8859-1 . No, aquí hay una confusión. En ISO-8859-1 (también conocido como Latin1), los 256 caracteres tienen de hecho el valor del punto de código del carácter Unicode correspondiente. Pero utf-8 tiene una codificación especial para todos los caracteres por encima de 0x7f y todos los caracteres que tienen un punto de código entre 0x80 y 0xff se representan como 2 bytes. Por ejemplo, el carácter é U+00e9 se representa como el byte único 0xe9 en ISO-8859-1, pero se representa como 2 bytes 0xc3 0xa9 en utf-8.
Más referencias en la página de wikipedia .