it-swarm.it

Come contare il numero di un carattere specifico in ogni riga?

Mi chiedevo come contare il numero di un carattere specifico in ciascuna riga da parte di alcune utility di elaborazione del testo?

Ad esempio, per contare " in ciascuna riga del seguente testo

"hello!" 
Thank you!

La prima riga ha due e la seconda riga ha 0.

Un altro esempio è contare ( in ogni riga.

97
Tim

Puoi farlo con sed e awk:

$ sed 's/[^"]//g' dat | awk '{ print length }'
2
0

Dove dat è il testo di esempio, sed elimina (per ogni riga) tutti i caratteri non - " E awk stampa per ogni riga la sua dimensione (ovvero length è equivalente a length($0), dove $0 indica la riga corrente).

Per un altro personaggio devi solo cambiare l'espressione sed. Ad esempio, per ( A:

's/[^(]//g'

Aggiornamento:sed è un po 'eccessivo per l'attività - tr è sufficiente. Una soluzione equivalente con tr è:

$ tr -d -c '"\n' < dat | awk '{ print length; }'

Ciò significa che tr elimina tutti i caratteri che non sono (-c Significa complemento) nel set di caratteri "\n.

115
maxschlepzig

Vorrei solo usare Awk

awk -F\" '{print NF-1}' <fileName>

Qui impostiamo il separatore di campo (con il flag -F) come carattere " allora tutto ciò che facciamo è stampare il numero di campi NF - 1. Il numero di occorrenze del carattere target sarà uno in meno del numero di campi separati.

Per i personaggi divertenti interpretati da Shell devi solo assicurarti di sfuggirli altrimenti la riga di comando proverà ad interpretarli. Quindi per entrambi " e ) devi uscire dal separatore di campo (con \).

52
Martin York

Utilizzando tr ard wc:

function countchar()
{
    while IFS= read -r i; do printf "%s" "$i" | tr -dc "$1" | wc -m; done
}

Uso:

$ countchar '"' <file.txt  #returns one count per line of file.txt
1
3
0

$ countchar ')'           #will count parenthesis from stdin
$ countchar '0123456789'  #will count numbers from stdin
15

Ancora un'altra implementazione che non si basa su programmi esterni, in bash, zsh, yash e alcune implementazioni/versioni di ksh:

while IFS= read -r line; do 
  line="${line//[!\"]/}"
  echo "${#line}"
done <input-file

Uso line="${line//[!(]}"per il conteggio (.

11
enzotib

Le risposte usando awk falliscono se il numero di corrispondenze è troppo grande (che sembra essere la mia situazione). Per la risposta da loki-astari , viene riportato il seguente errore:

awk -F" '{print NF-1}' foo.txt 
awk: program limit exceeded: maximum number of fields size=32767
    FILENAME="foo.txt" FNR=1 NR=1

Per la risposta da enzotib (e l'equivalente da manatwork ), si verifica un errore di segmentazione:

awk '{ gsub("[^\"]", ""); print length }' foo.txt
Segmentation fault

La soluzione sed di maxschlepzig funziona correttamente, ma è lenta (tempi sotto).

Alcune soluzioni non ancora suggerite qui. Innanzitutto, usando grep:

grep -o \" foo.txt | wc -w

E usando Perl:

Perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt

Ecco alcuni tempi per alcune delle soluzioni (ordinate dal più lento al più veloce); Ho limitato le cose a una linea qui. 'foo.txt' è un file con una riga e una stringa lunga che contiene 84922 corrispondenze.

## sed solution by [maxschlepzig]
$ time sed 's/[^"]//g' foo.txt | awk '{ print length }'
84922
real    0m1.207s
user    0m1.192s
sys     0m0.008s

## using grep
$ time grep -o \" foo.txt | wc -w
84922
real    0m0.109s
user    0m0.100s
sys     0m0.012s

## using Perl
$ time Perl -ne '$x+=s/\"//g; END {print "$x\n"}' foo.txt
84922
real    0m0.034s
user    0m0.028s
sys     0m0.004s

## the winner: updated tr solution by [maxschlepzig]
$ time tr -d -c '\"\n' < foo.txt |  awk '{ print length }'
84922
real    0m0.016s
user    0m0.012s
sys     0m0.004s
10
josephwb

Un'altra soluzione awk:

awk '{print gsub(/"/, "")}'
9

Un'altra possibile implementazione con awk e gsub:

awk '{ gsub("[^\"]", ""); print length }' input-file

La funzione gsub è l'equivalente di sed 's///g'.

Usa gsub("[^(]", "") per contare (.

8
enzotib

Ho deciso di scrivere un programma C perché ero annoiato.

Probabilmente dovresti aggiungere la convalida dell'input, ma a parte questo è tutto impostato.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
        char c = argv[1][0];
        char * line = NULL;
        size_t len = 0;
        while (getline(&line, &len, stdin) != -1)
        {
                int count = 0;
                char * s = line;
                while (*s) if(*s++ == c) count++;
                printf("%d\n",count);
        }
        if(line) free(line);
}
6
user606723

Per una stringa, il più semplice sarebbe con tr e wc (non è necessario eseguire l'overkill con awk o sed) - ma nota i commenti sopra su tr, conta i byte, non i caratteri -

echo $x | tr -d -c '"' | wc -m

dove $x è la variabile che contiene la stringa (non un file) da valutare.

6
Ocumo

Ecco un'altra soluzione C che richiede solo STD C e meno memoria:

#include <stdio.h>

int main(int argc, char **argv)
{
  if (argc < 2 || !*argv[1]) {
    puts("Argument missing.");
    return 1;
  }
  char c = *argv[1], x = 0;
  size_t count = 0;
  while ((x = getc(stdin)) != EOF)
    if (x == '\n') {
      printf("%zd\n", count);
      count = 0;
    } else if (x == c)
      ++count;
  return 0;
}
4
maxschlepzig

Forse una risposta più semplice, semplicemente imbarazzante sarebbe quella di usare la divisione. La divisione prende una stringa e la trasforma in un array, il valore restituito è il numero di elementi dell'array generati + 1.

Il seguente codice stamperà il numero di volte "appare su ogni riga.

awk ' {print (split($0,a,"\"")-1) }' file_to_parse

maggiori informazioni su split http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html

3
bleurp

Possiamo usare grep con regex per renderlo più semplice e potente.

Per contare un personaggio specifico.

$ grep -o '"' file.txt|wc -l

Per contare i caratteri speciali inclusi i caratteri degli spazi bianchi.

$ grep -Po '[\W_]' file.txt|wc -l

Qui stiamo selezionando qualsiasi carattere con [\S\s] e con -o opzione facciamo grep per stampare ogni corrispondenza (che è, ogni carattere) in una riga separata. E poi usa wc -l per contare ogni riga.

3
Kannan Mohan

Per una soluzione bash pura (tuttavia, è specifica per bash): Se $x è la variabile che contiene la stringa:

x2="${x//[^\"]/}"
echo ${#x2}

Il ${x// cosa rimuove tutti i caratteri tranne ", ${#x2} calcola la lunghezza di questo riposo.

(Suggerimento originale che utilizza expr che presenta problemi, vedere i commenti:)

expr length "${x//[^\"]/}"
2
Marian

Ecco un semplice Python per trovare il conteggio di " in ciascuna riga di un file:

#!/usr/bin/env python2
with open('file.txt') as f:
    for line in f:
        print line.count('"')

Qui abbiamo usato il metodo count di tipo str incorporato.

2
heemayl

Sostituisci a con il carattere da contare. L'output è il contatore per ogni riga.

Perl -nE 'say y!a!!'
2
JJoao

Confronto temporale delle soluzioni presentate (non una risposta)

L'efficienza delle risposte non è importante. Tuttavia, seguendo l'approccio di @josephwb, ho cercato di valutare tutte le risposte presentate.

Uso come input la traduzione portoghese di Victor Hugo "Les Miserables" (ottimo libro!) E conto le occorrenze di "a". La mia edizione ha 5 volumi, molte pagine ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

Le risposte C sono state compilate con gcc, (nessuna ottimizzazione).

Ogni risposta è stata eseguita 3 volte e scegli la migliore.

Non fidarti troppo di questi numeri (la mia macchina sta svolgendo altre attività, ecc., Ecc.). Condivido questi momenti con te, perché ho ottenuto risultati inaspettati e sono sicuro che ne troverai altri ...

  • 14 di 16 soluzioni a tempo impiegavano meno di 1 secondo; 9 in meno di 0,1 secondi, molti dei quali utilizzano tubi
  • 2 soluzioni, usando bash riga per riga, hanno elaborato le linee 30k creando nuovi processi, calcolando la soluzione corretta in 10s/20s.
  • grep -oP a è volte l'albero più veloce di grep -o a (10; 11 contro 12)
  • La differenza tra C e gli altri non è così grande come mi aspettavo. (7; 8 vs 2; 3)
  • (conclusioni benvenute)

(risulta in un ordine casuale)

=========================1 maxschlepzig
$ time sed 's/[^a]//g' mis.txt | awk '{print length}' > a2
real    0m0.704s ; user 0m0.716s
=========================2 maxschlepzig
$ time tr -d -c 'a\n' < mis.txt | awk '{ print length; }' > a12
real    0m0.022s ; user 0m0.028s
=========================3 jjoao
$ time Perl -nE 'say y!a!!' mis.txt  > a1
real    0m0.032s ; user 0m0.028s
=========================4 Stéphane Gimenez
$ function countchar(){while read -r i; do echo "$i"|tr -dc "$1"|wc -c; done }

$ time countchar "a"  < mis.txt > a3
real    0m27.990s ; user    0m3.132s
=========================5 Loki Astari
$ time awk -Fa '{print NF-1}' mis.txt > a4
real    0m0.064s ; user 0m0.060s
Error : several -1
=========================6 enzotib
$ time awk '{ gsub("[^a]", ""); print length }' mis.txt > a5
real    0m0.781s ; user 0m0.780s
=========================7 user606723
#include <stdio.h> #include <string.h> // int main(int argc, char *argv[]) ...  if(line) free(line); }

$ time a.out a < mis.txt > a6
real    0m0.024s ; user 0m0.020s
=========================8 maxschlepzig
#include <stdio.h> // int main(int argc, char **argv){if (argc < 2 || !*argv[1]) { ...  return 0; }

$ time a.out a < mis.txt > a7
real    0m0.028s ; user 0m0.024s
=========================9 Stéphane Chazelas
$ time awk '{print gsub(/a/, "")}'< mis.txt > a8
real    0m0.053s ; user 0m0.048s
=========================10 josephwb count total
$ time grep -o a < mis.txt | wc -w > a9
real    0m0.131s ; user 0m0.148s
=========================11 Kannan Mohan count total
$ time grep -o 'a' mis.txt | wc -l > a15
real    0m0.128s ; user 0m0.124s
=========================12 Kannan Mohan count total
$ time grep -oP 'a' mis.txt | wc -l > a16
real    0m0.047s ; user 0m0.044s
=========================13 josephwb Count total
$ time Perl -ne '$x+=s/a//g; END {print "$x\n"}'< mis.txt > a10
real    0m0.051s ; user 0m0.048s
=========================14 heemayl
#!/usr/bin/env python2 // with open('mis.txt') as f: for line in f: print line.count('"')

$ time pyt > a11
real    0m0.052s ; user 0m0.052s
=========================15 enzotib
$ time  while IFS= read -r line; do   line="${line//[!a]/}"; echo "${#line}"; done < mis.txt  > a13
real    0m9.254s ; user 0m8.724s
=========================16 bleurp
$ time awk ' {print (split($0,a,"a")-1) }' mis.txt > a14
real    0m0.148s ; user 0m0.144s
Error several -1
2
JJoao
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1

dove grep svolge tutto il lavoro pesante: riporta ogni personaggio trovato in ogni numero di riga. Il resto è solo quello di sommare il conteggio per riga e formattare l'output.

Rimuovi il -n e ottieni il conteggio per l'intero file.

Il conteggio di un file di testo 1.5Meg in meno di 0,015 secondi sembra veloce.
Funziona con caratteri (non byte).

1
user79743

Una soluzione per bash. Nessun programma esterno chiamato (più veloce per stringhe brevi).

Se il valore è in una variabile:

$ a='"Hello!"'

Questo stamperà quanti " contiene:

$ b="${a//[^\"]}"; echo "${#b}"
2
1
Isaac