Zastanawiałem się, jak policzyć liczbę określonego znaku w każdej linii przez niektóre narzędzia do przetwarzania tekstu?
Na przykład, aby policzyć "
w każdym wierszu następującego tekstu
"hello!"
Thank you!
Pierwszy wiersz ma dwa, a drugi wiersz ma 0.
Innym przykładem jest liczenie (
w każdej linii.
Odpowiedzi:
Możesz to zrobić za pomocą
sed
iawk
:Gdzie
dat
jest twój przykładowy tekst, sed usuwa (dla każdego wiersza) wszystkie"
znaki niebędące znakami iawk
drukuje dla każdego wiersza o jego rozmiarze (tzn.length
Jest równoważnelength($0)
, gdzie$0
oznacza bieżącą linię).W przypadku innej postaci wystarczy zmienić wyraz sed. Na przykład dla
(
:Aktualizacja:
sed
to rodzaj przesady w zadaniu -tr
wystarczy. Równoważne rozwiązanie ztr
:Oznacza to, że
tr
usuwa wszystkie znaki, które nie są (-c
oznaczają uzupełnienie) w zestawie znaków"\n
.źródło
tr
&wc
.ß
(UTF hex: c3 9f) (zamiast"
) działa zgodnie z oczekiwaniami, to znaczytr
,sed
aawk
nie uzupełnienia / wymiany / liczenie bez problemu - na systemie Ubuntu 10.04.tr
, w tym GNU tr i klasyczny Unix tr, działa na znakach jednobajtowych i nie jest zgodnych z Unicode. Cytat z Wikipedii tr (Unix) .. Wypróbuj ten fragment:echo "aā⧾c" | tr "ā⧾" b
... na Ubuntu 10.04 ...ß
to jeden bajt Rozszerzony znak łaciński i jest obsługiwany przeztr
... Prawdziwy problem nie polega na tym,tr
że nie obsługuje Unicode (ponieważ WSZYSTKIE znaki są Unicode), to tak naprawdętr
obsługuje tylko jeden bajt na raz ...Po prostu użyłbym awk
Tutaj ustawiamy separator pól (z flagą -F) na znak,
"
a następnie drukujemy liczbę pólNF
- 1. Liczba wystąpień znaku docelowego będzie o jeden mniejsza niż liczba oddzielnych pól.W przypadku zabawnych postaci, które są interpretowane przez powłokę, musisz tylko upewnić się, że uciec przed nimi, w przeciwnym razie linia poleceń spróbuje je zinterpretować. Więc dla obu
"
i)
musisz uciec z separatora pól (z\
).źródło
'
). Ponadto ma dziwne zachowanie z pustymi liniami."
więc czuję się zobowiązany, aby kod działał z nim. To zależy od tego, z jakiej powłoki korzystasz przy pogodzie, postać musi być uciekła, ale zarówno bash / tcsh będą musiały uciec ”-F'"'
.awk -F"$1" '{print NF==0?NF:NF-1}' filename
Za pomocą
tr
ardwc
:Stosowanie:
źródło
tr
nie obsługuje znaków, które używają więcej niż jednego bajtu .. patrz Wikipedia tr (Unix) .. tj.tr
nie jest zgodny z Unicode.$IFS
, w przeciwnym razieread
przycinimy je od początku i na końcu.echo
do arbitralnych danychtr
implementacje obsługują znaki wielobajtowe, ale i takwc -c
liczą bajty, a nie znaki (potrzebawc -m
znaków).Jeszcze inna realizacja, która nie opiera się na zewnętrznych programów, w
bash
,zsh
,yash
a niektóre implementacje / wersjeksh
:Użyj
line="${line//[!(]}"
do liczenia(
.źródło
eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
/
który nie jest potrzebny w bash. Czy to wymóg Ksh?/
jest potrzebne w starszych wersjach ksh, a IIRC również w starszych wersjach bash.Odpowiedzi przy użyciu
awk
niepowodzenia kończą się niepowodzeniem, jeśli liczba dopasowań jest zbyt duża (co dzieje się w mojej sytuacji). W przypadku odpowiedzi z loki-astari zgłaszany jest następujący błąd:W przypadku odpowiedzi z enzotibu (i odpowiednika manatwork ) występuje błąd segmentacji:
sed
Rozwiązanie przez maxschlepzig działa poprawnie, ale jest powolne (czasy poniżej).Niektóre rozwiązania nie zostały tu jeszcze zaproponowane. Po pierwsze, używając
grep
:I używając
perl
:Oto kilka harmonogramów kilku rozwiązań (uporządkowane od najwolniejszego do najszybszego); Tutaj ograniczyłem się do jednowarstwowych. „foo.txt” to plik z jedną linią i jednym długim łańcuchem, który zawiera 84922 dopasowań.
źródło
Inne
awk
rozwiązanie:źródło
Kolejna możliwa implementacja z awk i gsub:
Ta funkcja
gsub
jest odpowiednikiem sed's///g'
.Użyj
gsub("[^(]", "")
do liczenia(
.źródło
awk '{print gsub(/"/,"")}' input-file
byłoby wystarczające, ponieważ „Dla każdego podłańcucha pasującego do wyrażenia regularnego r w ciągu t zastąp łańcuch s i zwróć liczbę podstawień”. (man awk)Postanowiłem napisać program w C, bo się nudziłem.
Prawdopodobnie powinieneś dodać sprawdzanie poprawności danych wejściowych, ale poza tym wszystko jest ustawione.
źródło
free(line)
ponieważ wyjście z programu domyślnie zwalnia całą przydzieloną pamięć - wtedy jest miejsce nareturn 0;
...;). Nawet w przykładach pozostawienie kodu powrotu niezdefiniowanym nie jest dobrym stylem. Btw,getline
to rozszerzenie GNU - na wypadek, gdyby ktoś się zastanawiał.f
, która jest wywoływana kilka razy z innego kodu, wtedy musisz zadzwonićfree
po ostatnim wywołaniugetline
na końcu tej funkcjif
.W przypadku ciągu najprostsze byłoby z
tr
iwc
(nie trzeba przesadzać zawk
lubsed
) - ale zwróć uwagę na powyższe komentarze na temattr
, liczy bajty, a nie znaki -gdzie
$x
jest zmienną zawierającą ciąg (nie plik) do oceny.źródło
Oto inne rozwiązanie C, które potrzebuje tylko STD C i mniej pamięci:
źródło
\n
nie jest prawdziwą linią. To jest to samo zachowanie, co w mojej innej odpowiedzi sed / awk (tr / awk).Możemy użyć
grep
z,regex
aby uczynić to prostszym i wydajniejszym.Aby policzyć konkretny znak.
Aby policzyć znaki specjalne, w tym spacje.
Tutaj jesteśmy wybierając dowolny znak z
[\S\s]
iz-o
opcją wykonujemygrep
wydrukować każdą meczu (co jest, każdy znak) w osobnym wierszu. A następnie użyj,wc -l
aby policzyć każdą linię.źródło
"
jest w każdej linii; i dla innych znaków. zobacz jego pytanie, a także zaakceptowaną odpowiedź.Być może bardziej bezpośrednią, czystą odpowiedzią byłoby użycie podziału. Podział pobiera ciąg i przekształca go w tablicę, zwracaną wartością jest liczba wygenerowanych elementów tablicy + 1.
Poniższy kod wypisze liczbę razy „w każdym wierszu”.
więcej informacji na temat podziału http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html
źródło
Oto prosty skrypt Pythona do znalezienia liczby
"
w każdym wierszu pliku:Tutaj zastosowaliśmy
count
metodę typu wbudowanegostr
.źródło
Dla czystego rozwiązania bash (jednak jest ono specyficzne dla bash): Jeśli
$x
zmienna zawiera Twój ciąg:${x//
Rzeczą usuwa wszystkie znaki z wyjątkiem"
,${#x2}
oblicza długość tego odpoczynku.(Oryginalna sugestia, z
expr
której korzysta problem, patrz komentarze:)źródło
expr
i liczy bajty, a nie znaki. Z innymiexpr
:expr "x${x...}" : "x.*" - 1
Zastąp
a
znak, który chcesz policzyć. Wyjście jest licznikiem dla każdej linii.źródło
Porównanie czasowe prezentowanych rozwiązań (brak odpowiedzi)
Skuteczność odpowiedzi nie jest ważna. Niemniej jednak, postępując zgodnie z podejściem @josephwb, próbowałem ustalić czas na wszystkie przedstawione odpowiedzi.
Używam jako danych wejściowych portugalskiego tłumaczenia Victora Hugo „Les Miserables” (świetna książka!) I liczę wystąpienia „a”. Moje wydanie ma 5 tomów, wiele stron ...
Odpowiedzi C zostały skompilowane z gcc, (bez optymalizacji).
Każda odpowiedź została uruchomiona 3 razy i wybierz najlepszą.
Nie ufaj zbytnio tym liczbom (moja maszyna wykonuje inne zadania itp.). Dzielę się z Tobą tymi czasami, ponieważ otrzymałem nieoczekiwane wyniki i jestem pewien, że znajdziesz więcej ...
grep -oP a
drzewo jest wtedy szybszegrep -o a
(10; 11 vs 12)(wyniki w losowej kolejności)
źródło
gdzie grep wykonuje ciężkie podnoszenie: zgłasza każdy znak znaleziony przy każdym numerze linii. Reszta to po prostu zsumowanie liczby wierszy i sformatowanie wyniku.
Usuń
-n
i pobierz liczbę dla całego pliku.Liczenie pliku tekstowego 1,5 Meg w czasie krótszym niż 0,015 sekundy wydaje się szybkie.
I działa ze znakami (nie bajtami).
źródło
Rozwiązanie na bash. Nie został wywołany program zewnętrzny (szybszy w przypadku krótkich ciągów znaków).
Jeśli wartość jest w zmiennej:
Spowoduje to wydrukowanie, ile
"
zawiera:źródło