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
sedyawk:¿Dónde
datestá su texto de ejemplo? Sed elimina (para cada línea) todos los no"caracteres eawkimprime para cada línea su tamaño (lengthes decir, es equivalente alength($0), donde$0denota la línea actual).Para otro personaje solo tienes que cambiar la expresión sed. Por ejemplo para
(:Actualización:
sedes una especie de exageración para la tarea,tres suficiente. Una solución equivalente contres:Lo que significa que
trelimina todos los caracteres que no están (-csignifica complemento) en el conjunto de caracteres"\n.fuente
tr&wc.ß(utf hex: c3 9f) (en lugar de") funciona como se esperaba, es decirtr,sedyawkcomplementa / 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 quetrno maneja Unicode (porque TODOS los caracteres son Unicode), es realmente quetrsolo 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}' filenameUsando
trardwc:Uso:
fuente
trno maneja caracteres que usan más de un byte ... ver Wikipedia tr (Unix) ... es decir.trno es compatible con Unicode.$IFS, de lo contrarioreadlos recortará desde el principio y el final.echopara datos arbitrariostrimplementaciones admiten caracteres multibyte, perowc -ccuentan bytes, no caracteres de todos modos (se necesitanwc -mcaracteres).Sin embargo, otra aplicación que no se basa en programas externos, en
bash,zsh,yashy 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
awkfallan 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
sedsolució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
awksolución:fuente
Otra posible implementación con awk y gsub:
La función
gsubes el equivalente de sed's///g'.Úselo
gsub("[^(]", "")para contar(.fuente
awk '{print gsub(/"/,"")}' input-fileserí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,getlinees una extensión GNU, en caso de que alguien se pregunte.f, que se llama varias veces desde otro código, debe llamarfreedespués de la última llamadagetlineal final de esta funciónf.Para una cadena, lo más simple sería con
trywc(no es necesario exagerar conawkosed), pero tenga en cuenta los comentarios anteriores sobretr, cuenta bytes, no caracteres -donde
$xes 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
\nno es una línea real. Este es el mismo comportamiento que con mi otra respuesta sed / awk (tr / awk).Podemos utilizar
grepconregexpara 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-oopción que hagamosgreppara imprimir cada coincidencia (es decir, cada carácter) en una línea separada. Y luego usewc -lpara 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
countmétodo de tipo incorporadostr.fuente
Para una solución bash pura (sin embargo, es específica de bash): If
$xes 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
exprque tiene problemas, ver comentarios:)fuente
expry cuenta bytes, no caracteres. Con otrosexpr:expr "x${x...}" : "x.*" - 1Reemplazar
apor 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 aes 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
-ny 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