Me preguntaba cómo contar el número de un carácter específico en cada línea por algunas utilidades de procesamiento de texto.
Por ejemplo, para contar "
en cada línea del siguiente texto
"hello!"
Thank you!
La primera línea tiene dos y la segunda línea tiene 0.
Otro ejemplo es contar (
en cada línea.
Respuestas:
Puedes hacerlo con
sed
yawk
:¿Dónde
dat
está su texto de ejemplo? Sed elimina (para cada línea) todos los no"
caracteres eawk
imprime para cada línea su tamaño (length
es decir, es equivalente alength($0)
, donde$0
denota la línea actual).Para otro personaje solo tienes que cambiar la expresión sed. Por ejemplo para
(
:Actualización:
sed
es una especie de exageración para la tarea,tr
es suficiente. Una solución equivalente contr
es:Lo que significa que
tr
elimina todos los caracteres que no están (-c
significa complemento) en el conjunto de caracteres"\n
.fuente
tr
&wc
.ß
(utf hex: c3 9f) (en lugar de"
) funciona como se esperaba, es decirtr
,sed
yawk
complementa / reemplaza / cuenta sin problemas, en un sistema Ubuntu 10.04.tr
, incluidas GNU tr y Unix tr clásico, funcionan con caracteres de un solo byte y no son compatibles con Unicode. Citado de Wikipedia tr (Unix) . Pruebe este fragmento:echo "aā⧾c" | tr "ā⧾" b
... en Ubuntu 10.04 ...ß
es un solo byte El carácter latino extendido y es manejado portr
... El verdadero problema aquí no es quetr
no maneja Unicode (porque TODOS los caracteres son Unicode), es realmente quetr
solo maneja un byte a la vez ...Yo solo usaría awk
Aquí configuramos el separador de campo (con el indicador -F) para que sea el carácter,
"
entonces todo lo que hacemos es imprimir el número de camposNF
- 1. El número de ocurrencias del carácter de destino será uno menos que el número de campos separados.Para los personajes divertidos que son interpretados por el shell, solo necesita asegurarse de escapar de ellos; de lo contrario, la línea de comandos intentará interpretarlos. Por lo tanto
"
y)
tiene que escapar el separador de campo (con\
).fuente
'
). Además, tiene un comportamiento extraño con líneas vacías."
así que me siento obligado a hacer que el código funcione con él. Depende del tipo de capa que esté utilizando, el personaje necesita escapar, pero bash / tcsh necesitará escapar "-F'"'
.awk -F"$1" '{print NF==0?NF:NF-1}' filename
Usando
tr
ardwc
:Uso:
fuente
tr
no maneja caracteres que usan más de un byte ... ver Wikipedia tr (Unix) ... es decir.tr
no es compatible con Unicode.$IFS
, de lo contrarioread
los recortará desde el principio y el final.echo
para datos arbitrariostr
implementaciones admiten caracteres multibyte, perowc -c
cuentan bytes, no caracteres de todos modos (se necesitanwc -m
caracteres).Sin embargo, otra aplicación que no se basa en programas externos, en
bash
,zsh
,yash
y algunas implementaciones / versiones deksh
:Úselo
line="${line//[!(]}"
para contar(
.fuente
eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
/
que no es necesario en bash. Es un requisito ksh?/
es necesario en versiones anteriores de ksh, y IIRC también en versiones anteriores de bash.Las respuestas que usan
awk
fallan si el número de coincidencias es demasiado grande (que es mi situación). Para la respuesta de loki-astari , se informa el siguiente error:Para la respuesta de enzotib (y el equivalente de manatwork ), se produce una falla de segmentación:
La
sed
solución de maxschlepzig funciona correctamente, pero es lenta (tiempos a continuación).Algunas soluciones aún no sugeridas aquí. Primero, usando
grep
:Y usando
perl
:Aquí hay algunos tiempos para algunas de las soluciones (ordenadas de la más lenta a la más rápida); Limité las cosas a frases sencillas aquí. 'foo.txt' es un archivo con una línea y una cadena larga que contiene 84922 coincidencias.
fuente
Otra
awk
solución:fuente
Otra posible implementación con awk y gsub:
La función
gsub
es el equivalente de sed's///g'
.Úselo
gsub("[^(]", "")
para contar(
.fuente
awk '{print gsub(/"/,"")}' input-file
sería suficiente, como "Para cada subcadena que coincida con la expresión regular r en la cadena t, sustituya la cadena s y devuelva el número de sustituciones". (man awk)Decidí escribir un programa en C porque estaba aburrido.
Probablemente debería agregar validación de entrada, pero aparte de eso, todo está configurado.
fuente
free(line)
porque salir del programa implícitamente libera toda la memoria asignada, entonces hay lugar para unreturn 0;
...;). Incluso en los ejemplos, no es un buen estilo dejar el código de retorno sin definir. Por cierto,getline
es una extensión GNU, en caso de que alguien se pregunte.f
, que se llama varias veces desde otro código, debe llamarfree
después de la última llamadagetline
al final de esta funciónf
.Para una cadena, lo más simple sería con
tr
ywc
(no es necesario exagerar conawk
osed
), pero tenga en cuenta los comentarios anteriores sobretr
, cuenta bytes, no caracteres -donde
$x
es la variable que contiene la cadena (no un archivo) para evaluar.fuente
Aquí hay otra solución C que solo necesita STD C y menos memoria:
fuente
\n
no es una línea real. Este es el mismo comportamiento que con mi otra respuesta sed / awk (tr / awk).Podemos utilizar
grep
conregex
para que sea más sencillo y potente.Para contar un personaje específico.
Para contar caracteres especiales, incluidos los espacios en blanco.
Aquí estamos seleccionando cualquier carácter con
[\S\s]
y con la-o
opción que hagamosgrep
para imprimir cada coincidencia (es decir, cada carácter) en una línea separada. Y luego usewc -l
para contar cada línea.fuente
"
hay en cada línea; y para cualquier otro personaje. vea su pregunta y también aceptó la respuesta.Tal vez una respuesta más directa y puramente mala sería usar split. Split toma una cadena y la convierte en una matriz, el valor de retorno es el número de elementos de la matriz generados + 1.
El siguiente código imprimirá el número de veces "aparece en cada línea.
más información sobre split http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html
fuente
Aquí hay un script simple de Python para encontrar el recuento
"
en cada línea de un archivo:Aquí hemos utilizado el
count
método de tipo incorporadostr
.fuente
Para una solución bash pura (sin embargo, es específica de bash): If
$x
es la variable que contiene su cadena:La
${x//
cosa elimina todos los caracteres excepto"
,${#x2}
calcula la duración de este descanso.(Sugerencia original usando la
expr
que tiene problemas, ver comentarios:)fuente
expr
y cuenta bytes, no caracteres. Con otrosexpr
:expr "x${x...}" : "x.*" - 1
Reemplazar
a
por el carácter a contar. La salida es el contador para cada línea.fuente
Comparación de tiempos de las soluciones presentadas (no es una respuesta)
La eficiencia de las respuestas no es importante. Sin embargo, siguiendo el enfoque de @josephwb, intenté cronometrar todas las respuestas presentadas.
Utilizo como entrada la traducción portuguesa de Victor Hugo "Les Miserables" (¡gran libro!) Y cuento las ocurrencias de "a". Mi edición tiene 5 volúmenes, muchas páginas ...
Las respuestas C se compilaron con gcc (sin optimizaciones).
Cada respuesta se ejecutó 3 veces y elige la mejor.
No confíe demasiado en estos números (mi máquina está haciendo otras tareas, etc., etc.). Comparto estos momentos con ustedes, porque obtuve algunos resultados inesperados y estoy seguro de que encontrarán más ...
grep -oP a
es el árbol veces más rápido quegrep -o a
(10; 11 vs 12)(resultados en un orden aleatorio)
fuente
donde grep hace todo el trabajo pesado: informa cada carácter encontrado en cada número de línea. El resto es solo para sumar el recuento por línea y formatear la salida.
Elimine
-n
y obtenga el recuento de todo el archivo.Contar un archivo de texto de 1.5Meg en menos de 0.015 segundos parece rápido.
Y funciona con caracteres (no bytes).
fuente
Una solución para bash. No se llama a un programa externo (más rápido para cadenas cortas).
Si el valor está en una variable:
Esto imprimirá cuántos
"
contiene:fuente