Comando "wc -c" y "wc -m" en linux

24

Tengo un archivo de texto, su contenido es:

i k k

Cuando uso wc -mpara contar números de caracteres en este archivo, el resultado es 7 .

Pregunta 1: ¿Pero por qué obtuve 7, no debería obtener " 6 " suponiendo que cuenta el carácter de " fin de línea "?

Pregunta 2: ¿Cómo funciona exactamente wc -m?

Pregunta 3: Cuando uso wc -c(para contar números de bytes), tengo el mismo resultado que wc -m, ¿cuál es el punto de tener dos opciones diferentes ? Hacen exactamente el mismo trabajo, ¿no? Si no, ¿cuál es la diferencia y cómo wc -cfunciona?

SWIIWII
fuente
1
Lee Joel en el Absoluto Cada mínimos de software de desarrollo de software absolutamente, positivamente debe saber sobre Unicode y juegos de caracteres (Sin excusas!) Para una explicación acerca de los personajes, la codificación de caracteres y juegos de caracteres
phuclv
1
También podría haber obtenido 7 si el archivo provenía de Windows con terminaciones de línea CRLF
Chris H

Respuestas:

36

De hecho, debería tener solo 6 caracteres allí. Intenta correr

cat -A filename

Para ver los caracteres que no se imprimen de su archivo. Debes tener algo extra. Si hago un archivo como el tuyo, veo

i k k$

¿Pusiste un espacio? Eso haría 7: i k k $o tal vez tiene una nueva línea:

i k k$
$

que también es 7

Como usted dice

wc -m

cuenta personajes y

wc -c

cuenta bytes. Si todos sus caracteres son parte del conjunto de caracteres ASCII, entonces solo habrá 1 byte por carácter, por lo que obtendrá el mismo recuento de ambos comandos.

Pruebe un archivo con caracteres no ASCII:

$ echo ك > testfile
$ wc -m testfile
2 testfile
$ wc -c testfile
3 testfile

¡Ajá! Más bytes que caracteres ahora.

Zanna
fuente
3
Utilicé el comando " cat -A " y finalmente descubrí que tengo un espacio antes del carácter de " fin de línea " ( $ ). Es por eso que obtuve 7 en lugar de 6. Gracias, el " gato-A " ayudó mucho.
SWIIWII
2
@SWIIWII Sí, acabo de agregar eso a mi respuesta, ya que pensé que probablemente sería eso :)
Zanna
1
También se contó el carácter de nueva línea. Incluso si no es visible, sigue siendo un personaje y cuenta en el archivo como una porción de datos. Buen uso de cat -A por cierto. Una vez también podría usar hexdump o xxd para hacer lo mismo
Sergiy Kolodyazhnyy
@Serg sí, y cat -Alo demostraría también. Agregué a mi respuesta, gracias :)
Zanna
Coloque código @SWIIWII entre comillas sencillas `likethis`para que sea legible, no ponerlo en negrita
phuclv
2
$ locale charmap
UTF-8

En mi entorno actual, el conjunto de caracteres es UTF-8, es decir, los caracteres están codificados con 1 a 4 bytes por carácter (aunque debido a que la definición original del código de caracteres permitido UTF-8 apunta hasta 0x7fffffff, la mayoría de las herramientas reconocerían UTF- Secuencias de 8 bytes de hasta 6 bytes).

En ese conjunto de caracteres, todos los caracteres de Unicode están disponibles, a ase codifica como el valor de byte 65, a como los 3 bytes 228 185 149 y écomo la secuencia de dos bytes 195 169 por ejemplo.

$ printf 乕 | wc -mc
  1       3
$ printf a | wc -mc
  1       1

Ahora:

$ export fr_FR.iso885915@euro
$ locale charmap
ISO-8859-15

Modifiqué mi entorno, donde el juego de caracteres ahora es ISO-8859-15 (también se han modificado otras cosas como el idioma, el símbolo de moneda, el formato de fecha, la colección de esas configuraciones regionales se conoce como la configuración regional ). Necesito iniciar un nuevo emulador de terminal en ese entorno para que adapte su representación de caracteres a la nueva configuración regional.

ISO-8859-15 es un conjunto de caracteres de un solo byte, lo que significa que solo tiene 256 caracteres (en realidad, incluso menos de los que están cubiertos). Ese conjunto de caracteres en particular se utiliza para los idiomas de Europa occidental, ya que cubre la mayoría de sus idiomas (y el símbolo del euro).

Tiene el acarácter con el valor de byte 65 como en UTF-8 o ASCII, también tiene el écarácter (como se usa comúnmente en francés o español, por ejemplo) pero con el valor de byte 233, no tiene el carácter 乕.

En ese entorno, wc -cy wc -msiempre dará el mismo resultado.

En Ubuntu, como en la mayoría de los sistemas modernos similares a Unix, el valor predeterminado suele ser UTF-8, ya que es el único conjunto de caracteres (y codificación) compatible que cubre todo el rango de Unicode.

Existen otras codificaciones de caracteres de varios bytes, pero no son tan compatibles con Ubuntu y tienes que pasar por aros para poder generar una configuración regional con ellas, y si lo haces, encontrarás que muchas cosas no funcionar correctamente.

En efecto, en Ubuntu, los conjuntos de caracteres son de un solo byte o UTF-8.

Ahora, algunas notas más:

En UTF-8, no todas las secuencias de bytes forman caracteres válidos. Por ejemplo, todos los caracteres UTF-8 que no son ASCII se forman con bytes que tienen el conjunto de 8 bits, pero donde solo el primero tiene el conjunto de 7 bits.

Si tiene una secuencia de bytes con el conjunto de 8 bits, ninguno de los cuales tiene el conjunto de 7 bits, entonces eso no se puede traducir a un carácter. Y es entonces cuando comienzas a tener problemas e inconsistencias, ya que el software no sabe qué hacer con ellos. Por ejemplo:

$ printf '\200\200\200' | wc -mc
      0       3
$ printf '\200\200\200' | grep -q . || echo no
no

wcy grepno encuentra ningún personaje allí sino:

$ x=$'\200\200\200' bash -c 'echo "${#x}"'
3

bash encuentra 3. Cuando no puede asignar una secuencia de bytes a un carácter, considera cada byte como un carácter.

Puede ser aún más complicado ya que hay puntos de código en Unicode que no son válidos como caracteres, y algunos que no son caracteres , y dependiendo de la herramienta, su codificación UTF-8 puede o no considerarse como un carácter.

Otra cosa a tener en cuenta es la diferencia entre el personaje y el gráfico, y cómo se representan.

$ printf 'e\u301\u20dd\n'
é⃝
$ printf 'e\u301\u20dd' | wc -mc
      3       6

Allí, codificamos 3 caracteres como 6 bytes representados como un gráfico, porque tenemos 3 caracteres combinados juntos (un carácter base, un acento agudo combinado y un círculo envolvente combinado).

La implementación de GNU de wccomo se encuentra en Ubuntu tiene un -Linterruptor para indicarle el ancho de visualización de la línea más ancha en la entrada:

$ printf 'e\u301\u20dd\n' | wc -L
1

También encontrarás que algunos personajes ocupan 2 celdas en ese cálculo de ancho como nuestro personaje de arriba:

$ echo 乕 | wc -L
2

En conclusión: en la palabra más salvaje, byte, carácter y gráfico no son necesariamente lo mismo.

Stéphane Chazelas
fuente
1

La diferencia entre wc -cy wc -mes que en un entorno local con caracteres multibyte (por ejemplo, UTF8), el primero cuenta bytes, mientras que el segundo cuenta caracteres. Considere el siguiente archivo:

$ hexdump -C dummy.txt 
00000000  78 79 cf 80 0a                                    |xy...|

(para aquellos que no hablan UTF8, esas son las letras 'x', 'y' y 'π', seguidas de una nueva línea). Tiene cinco bytes de longitud:

$ wc -c dummy.txt 
5 dummy.txt

pero solo cuatro caracteres de largo:

$ wc -m dummy.txt 
4 dummy.txt
marca
fuente
O considere incluso UTF-32 donde cada carácter tiene 4 bytes.
Jörg W Mittag