¿Ancho del campo printf: bytes o caracteres?

SOURCE 6392 palabras cunicodeglibc
Soporte de la serie

El problema que quiero resolver

printf/fprintf/sprintf
El campo ancho en el descriptor de formato. Lo dudo un poco.
Para parámetros de matriz de caracteres (no anchos):
¿El campo ancho se refiere a bytes o caracteres?
Si char Array
¿Corresponde (por ejemplo) a la cadena UTF - 8 original?
Sé que normalmente debería usar algunos tipos de caracteres anchos,
Ese no es el punto)
Por ejemplo, en
char s[] = "ni\xc3\xb1o";  // utf8 encoded "niño"
fprintf(f,"%5s",s);
Esta función sólo debe producir 5 bytes
(carácter C normal) (y usted es responsable de errores)
¿O algún otro problema (si dos bytes producen caracteres de texto)?
O debería intentar calcular la longitud del "carácter de texto"
¿Parte de la matriz? ¿Decorarlo de acuerdo a su ubicación actual?
En este caso, esto equivale a encontrar una cadena
4 caracteres UNICODE, por lo que a ñadirá un espacio de relleno).
Actualización: Estoy de acuerdo con la respuesta, es lógico que la familia printf no lo haga
Distingue entre caracteres C normales y bytes. El problema es que mi Glibc no parece tener
Para respetar plenamente esta idea, si el entorno lingüístico
Uno es (actualmente el más utilizado) Lang/LC Ctype = en US. UTF - 8
Por ejemplo:
#include<stdio.h>
#include<locale.h>
main () {
        char * locale = setlocale(LC_ALL, ""); /* I have LC_CTYPE="en_US.UTF-8" */
        char s[] = {'n','i', 0xc3,0xb1,'o',0}; /* "niño" in utf8: 5 bytes, 4 unicode chars */
        printf("|%*s|\n",6,s); /* this should pad a blank - works ok*/
        printf("|%.*s|\n",4,s); /* this should eat a char - works ok */
        char s3[] = {'A',0xb1,'B',0}; /* this is not valid UTF8 */
        printf("|%s|\n",s3);     /* print raw chars - ok */
        printf("|%.*s|\n",15,s3);     /* panics (why???) */
}
Por lo tanto, incluso si se establece un entorno de lenguaje no posix - C, printf parece tener el concepto correcto de ancho de cálculo: bytes (caracteres comunes c) en lugar de caracteres UNICODE. No importa. Sin embargo, cuando se da una matriz de caracteres no decodificables en su configuración local, se asusta silenciosamente (se aborta - no se imprime nada después del primer "|"y no hay mensajes de error)... Sólo si necesita calcular algún ancho. No entiendo por qué incluso intenta decodificar cadenas en UTF - 8, y no necesita/debe hacerlo. ¿Es un error en Glibc?
Prueba con Glibc 2.11.1 (Fedora 12) (también se puede utilizar Glibc 2.3.6)
Nota: esto no tiene nada que ver con los problemas de visualización del terminal - Usted puede comprobar la salida a través de una conexión de tubería a od: $ ./a.out | od -t cx1. Aquí está mi salida:
0000000   |       n   i 303 261   o   |  \n   |   n   i 303 261   |  \n
         7c  20  6e  69  c3  b1  6f  7c  0a  7c  6e  69  c3  b1  7c  0a
0000020   |   A 261   B   |  \n   |
         7c  41  b1  42  7c  0a  7c
Actualización 2 (mayo de 2015): en una versión más reciente de Glibc, este comportamiento sospechoso has been fixed (parece comenzar con 2.17). Con glibc-2.17-21.fc19, creo que está bien.

La mejor solución

hará que la salida sea de cinco bytes. Hay cinco más. En ISO C, no hay diferencia entre caracteres y bytes. Los bytes no son necesariamente 8 bits, sino que se definen como el ancho del carácter.
El término ISO para un valor de 8 bits es un byte de 8 bits.
En el entorno C, la cadena nino tiene en realidad cinco caracteres de ancho (por supuesto, no hay Terminator vacío). Si sólo se muestran cuatro símbolos en el terminal, es casi seguro que es una función del terminal, no una función de salida de C.
No estoy diciendo que la implementación C no pueda manejar UNICODE. Si el bit de carácter se define como 32, puede implementar fácilmente UTF - 32. UTF - 8 puede ser más difícil porque es una codificación de longitud variable, pero hay una solución a casi cualquier problema: -)
Dependiendo de su actualización, puede tener problemas. Sin embargo, en mi configuración, no vi el comportamiento que describió en la misma configuración local. En mi ejemplo, tengo la misma salida en las dos últimas declaraciones printf.
Si su configuración simplemente detiene la salida después del primer | (creo que esto es lo que usted llama abortar, pero si usted se refiere a todo el programa abortar, que es más grave), voy a plantear el problema con GNU (pruebe su programa en particular primero). Usted ha hecho todo el trabajo importante, como generar un caso de prueba mínimo, para que si su distribución no está a la altura de la última versión (la mayoría de ellos no), incluso alguien debería estar feliz de ejecutarlo en la última versión.
Por cierto, no estoy seguro de lo que quieres decir con comprobar la salida od. En mi sistema, tengo:
pax> ./qq | od -t cx1
0000000   |       n   i 303 261   o   |  \n   |   n   i 303 261   |  \n
         7c  20  6e  69  c3  b1  6f  7c  0a  7c  6e  69  c3  b1  7c  0a
0000020   |   A 261   B   |  \n   |   A 261   B   |  \n
         7c  41  b1  42  7c  0a  7c  41  b1  42  7c  0a
0000034
Por lo tanto, puede ver que el flujo de salida contiene UTF - 8, lo que significa que el Programa terminal debe explicar esto. C/Glibc no modificó la salida en absoluto, así que podría haber malinterpretado lo que quieres decir.
Aunque me acabo de dar cuenta de que usted podría decir que su salida od también tiene una barra de inicio en esa línea (A diferencia de la mía, no parece tener ningún problema), lo que significa que está mal en C/Glibc, No hay nada malo con que el terminal borre los caracteres en silencio (honestamente, espero que el terminal borre toda la línea o sólo los caracteres problemáticos (es decir, salida |A) - de hecho, usted acaba de conseguir | parece haber eliminado el problema del terminal. Sírvanse aclarar este punto.