2011-12-25 33 views
32

Dado un archivo con los datos de este tipo (es decir, archivos) stores.datUNIX - contar las apariciones de caracteres por línea/campo

sid|storeNo|latitude|longitude 
2tt|1|-28.0372000t0|153.42921670 
9|2t|-33tt.85t09t0000|15t1.03274200 

¿Cuál es el comando que devolvería el número de ocurrencias de la 't' personaje por linea

por ejemplo. devolvería:

count lineNum 
    4  1 
    3  2 
    6  3 

Además, para hacerlo mediante el recuento de apariciones por el campo lo que es el comando para devolver los siguientes resultados?

por ejemplo. entrada de la columna 2 y el carácter 't'

count lineNum 
    1  1 
    0  2 
    1  3 

por ejemplo. la entrada de la columna 3 y el carácter 't'

count lineNum 
    2  1 
    1  2 
    4  3 
+0

echa un vistazo a http://www.gnu.org/software/gawk/manual/gawk.html es una herramienta de Unix muy poderosa – Chris

+0

http://unix.stackexchange.com/questions/18736/how-to -count-the-number-of-a-specific-character-in-each-line –

Respuesta

38

Contar aparición de un caracter en cada línea que puede hacer:

awk -F'|' 'BEGIN{print "count", "lineNum"}{print gsub(/t/,"") "\t" NR}' file 
count lineNum 
4  1 
3  2 
6  3 

Contar aparición de un caracter por campo/columna que puede hacer:

columna 2:

awk -F'|' -v fld=2 'BEGIN{print "count", "lineNum"}{print gsub(/t/,"",$fld) "\t" NR}' file 
count lineNum 
1  1 
0  2 
1  3 

columna 3:

awk -F'|' -v fld=3 'BEGIN{print "count", "lineNum"}{print gsub(/t/,"",$fld) "\t" NR}' file 
count lineNum 
2  1 
1  2 
4  3 
  • gsub() valor de retorno de función es el número de sustitución hecho. Entonces usamos eso para imprimir el número.
  • NR contiene el número de línea, así que lo usamos para imprimir el número de línea.
  • Para las ocurrencias de impresión de un campo particular, creamos una variable fld y ponemos el número de campo desde el que deseamos extraer recuentos.
+0

¡increíble! gracias por seguir con eso, eso funciona. – toop

+0

Imprime "0" (ocurrencias) también, lo que podría no desearse en la salida –

+0

@TarunSapra En realidad se muestra como resultado esperado en la pregunta. –

3

Una posible solución utilizando perl:

contenido de script.pl:

use warnings; 
use strict; 

## Check arguments: 
## 1.- Input file 
## 2.- Char to search. 
## 3.- (Optional) field to search. If blank, zero or bigger than number 
##  of columns, default to search char in all the line. 
(@ARGV == 2 || @ARGV == 3) or die qq(Usage: perl $0 input-file char [column]\n); 

my ($char,$column); 

## Get values or arguments. 
if (@ARGV == 3) { 
     ($char, $column) = splice @ARGV, -2; 
} else { 
     $char = pop @ARGV; 
     $column = 0; 
} 

## Check that $char must be a non-white space character and $column 
## only accept numbers. 
die qq[Bad input\n] if $char !~ m/^\S$/ or $column !~ m/^\d+$/; 

print qq[count\tlineNum\n]; 

while (<>) { 
     ## Remove last '\n' 
     chomp; 

     ## Get fields. 
     my @f = split /\|/; 

     ## If column is a valid one, select it to the search. 
     if ($column > 0 and $column <= scalar @f) { 
       $_ = $f[ $column - 1]; 
     } 

     ## Count. 
     my $count = eval qq[tr/$char/$char/]; 

     ## Print result. 
     printf qq[%d\t%d\n], $count, $.; 
} 

El script acepta tres parámetros:

  1. archivos de entrada
  2. Char a búsqueda
  3. Columna a buscar: si la columna es un dígito malo, busca toda la línea.

Ejecución del script sin argumentos:

perl script.pl 
Usage: perl script.pl input-file char [column] 

Con argumentos y su salida:

Aquí 0 es un mal columna, se busca en toda la línea.

perl script.pl stores.dat 't' 0 
count lineNum 
4  1 
3  2 
6  3 

Aquí búsquedas en la columna 1.

perl script.pl stores.dat 't' 1 
count lineNum 
0  1 
2  2 
0  3 

Aquí se busca en la columna 3.

perl script.pl stores.dat 't' 3 
count lineNum 
2  1 
1  2 
4  3 

th no es un char.

perl script.pl stores.dat 'th' 3 
Bad input 
+1

wow, tengo que aprender Perl – toop

+0

Me gusta mucho, pero aceptando la otra respuesta para una integración más sencilla con bash – toop

0
cat stores.dat | awk 'BEGIN {FS = "|"}; {print $1}' | awk 'BEGIN {FS = "\t"}; {print NF}' 

Dónde $1 habría un número de columna que desea contar.

2

No hay necesidad de awk o perl, sólo que con bash y utilidades estándar de Unix:

cat file | tr -c -d "t\n" | cat -n | 
    { echo "count lineNum" 
    while read num data; do 
     test ${#data} -gt 0 && printf "%4d %5d\n" ${#data} $num 
    done; } 

Y para una columna en particular:

cut -d "|" -f 2 file | tr -c -d "t\n" | cat -n | 
    { echo -e "count lineNum" 
    while read num data; do 
     test ${#data} -gt 0 && printf "%4d %5d\n" ${#data} $num 
    done; } 

e incluso podemos evitar tr y los cat s:

echo "count lineNum" 
num=1 
while read data; do 
    new_data=${data//t/} 
    count=$((${#data}-${#new_data})) 
    test $count -gt 0 && printf "%4d %5d\n" $count $num 
    num=$(($num+1)) 
done < file 

y evento el corte:

echo "count lineNum" 
num=1; OLF_IFS=$IFS; IFS="|" 
while read -a array_data; do 
    data=${array_data[1]} 
    new_data=${data//t/} 
    count=$((${#data}-${#new_data})) 
    test $count -gt 0 && printf "%4d %5d\n" $count $num 
    num=$(($num+1)) 
done < file 
IFS=$OLF_IFS 
31
grep -n -o "t" stores.dat | sort -n | uniq -c | cut -d : -f 1 

da casi exactamente la salida que desea:

4 1 
    3 2 
    6 3 

Gracias a @ raghav-bhushan para la grep -o pista, lo que es un indicador útil. La bandera -n también incluye el número de línea.

+1

Esta es una solución mucho más elegante y general. –

+2

+1 por no hacer que me escriba todo lo que awk – slf

+1

Creo que el 'sort -n' podría ser prescindido de - ¿no es la salida en orden de número de línea de todos modos? –

1
$ cat -n test.txt 
1 test 1 
2 you want 
3 void 
4 you don't want 
5 ttttttttttt 
6 t t t t t t 

$ awk '{n=split($0,c,"t")-1;if (n!=0) print n,NR}' test.txt 
2 1 
1 2 
2 4 
11 5 
6 6 
0

También podría dividir la línea o campo con "t" y comprobar la longitud de la matriz resultante - 1. Establecer la variable col a 0 para la línea o 1 a 3 para columnas:

awk -F'|' -v col=0 -v OFS=$'\t' 'BEGIN { 
    print "count", "lineNum" 
}{ 
    split($col, a, "t"); print length(a) - 1, NR 
} 
' stores.dat 
2
awk '{gsub("[^t]",""); print length($0),NR;}' stores.dat 

La llamada a gsub() borra todo lo que no está en la línea, luego solo imprime la longitud de lo que queda y el número de línea actual.

¿Quieres hacerlo solo para la columna 2?

awk 'BEGIN{FS="|"} {gsub("[^t]","",$2); print NR,length($2);}' stores.dat 
9

Para contar las apariciones de un carácter por línea:

$ awk -F 't' '{print NF-1, NR}' input.txt 
4 1 
3 2 
6 3 

esto establece separador de campo con el carácter que necesita ser contada, a continuación, utiliza el hecho de que el número de campos es uno mayor que el número de separadores.

Contar ocurrencias en una columna particular cut que la primera columna:

$ cut -d '|' -f 2 input.txt | awk -F 't' '{print NF-1, NR}' 
1 1 
0 2 
1 3 

$ cut -d '|' -f 3 input.txt | awk -F 't' '{print NF-1, NR}' 
2 1 
1 2 
4 3 
0
perl -e 'while(<>) { $count = tr/t//; print "$count ".++$x."\n"; }' stores.dat 

Otra Perl respuesta yay! La función tr/t // devuelve el recuento del número de veces que se produjo la traducción en esa línea, en otras palabras, el número de veces tr encontró el carácter 't'. ++ $ x mantiene el recuento de número de línea.

Cuestiones relacionadas