Есть ли простой способ подсчета символов в словах в файле, из терминала?

8

У меня есть 100 миллионов строк в моем файле.

Каждая строка имеет только один столбец.

например

aaaaa
bb
cc
ddddddd
ee

Я хотел бы перечислить количество символов

Нравится

2 character words - 3
5 character words - 1
7 character words - 1

и т.п.

Есть ли простой способ сделать это в терминале?

Гири
источник

Ответы:

20
$ awk '{ print length }' file | sort -n | uniq -c | awk '{ printf("%d character words: %d\n", $2, $1) }'
2 character words: 3
5 character words: 1
7 character words: 1

Первый awkфильтр просто напечатает длину каждой строки в вызываемом файле file. Я предполагаю, что этот файл содержит одно слово в строке.

В sort -n(сортировка линии с выхода awkчисленно в порядке возрастания) и uniq -c(подсчитать количество раз , каждая строка происходит последовательно) , а затем создаст следующий вывод из что для приведенных данных:

   3 2
   1 5
   1 7

Затем он анализируется вторым awkсценарием, который интерпретирует каждую строку как «число X строк, содержащих символы Y» и выдает желаемый результат.


Альтернативное решение состоит в том, чтобы сделать все это awkи вести подсчет длин в массиве. Это компромисс между эффективностью, удобочитаемостью / простотой понимания (и, следовательно, удобством обслуживания), какое решение является «лучшим».

Альтернативное решение:

$ awk '{ len[length]++ } END { for (i in len) printf("%d character words: %d\n", i, len[i]) }' file
2 character words: 3
5 character words: 1
7 character words: 1
Кусалананда
источник
Нет необходимости сортировать в awk (числовые индексы сортируются по умолчанию) (быстрее).
Исаак
@ Стрелка, я знаю. Я закомментировал это решение в своем ответе, потому что Sundeep опередил меня за несколько секунд. Я также упоминаю об этом в своем последнем абзаце.
Кусалананда
Я считаю, что комментарий должен быть полезен для пользователей решений (не включенных в ваш ответ (или ответ Sundeep) :-)…). В противном случае: включите комментарий с тем же эффектом в свой ответ, и я с удовольствием удалю мои комментарии. :-)
Исаак
10

Еще один способ сделать все это в awkодиночку

$ awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' ip.txt 
2 character words - 3
5 character words - 1
7 character words - 1
  • words[length()]++ использовать длину строки ввода в качестве ключа, чтобы сохранить счет
  • END{for(k in words)print k " character words - " words[k]} после обработки всех строк выведите содержимое массива в нужном формате


Сравнение производительности, выбранные номера являются лучшими из двух прогонов

$ wc words.txt
 71813  71813 655873 words.txt
$ perl -0777 -ne 'print $_ x 1000' words.txt > long_file.txt
$ du -h --apparent-size long_file.txt
626M    long_file.txt

$ time awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m20.632s
user    0m20.464s
sys     0m0.108s

$ time perl -lne '$h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}' long_file.txt > t2

real    0m19.749s
user    0m19.640s
sys     0m0.108s

$ time awk '{ print length }' long_file.txt | sort -n | uniq -c | awk '{ printf("%d character words - %d\n", $2, $1) }' > t3

real    1m23.294s
user    1m24.952s
sys     0m1.980s

$ diff -s <(sort t1) <(sort t2)
Files /dev/fd/63 and /dev/fd/62 are identical
$ diff -s <(sort t1) <(sort t3)
Files /dev/fd/63 and /dev/fd/62 are identical

Если файл содержит только символы ASCII,

$ time LC_ALL=C awk '{words[length()]++} END{for(k in words)print k " character words - " words[k]}' long_file.txt > t1

real    0m15.651s
user    0m15.496s
sys     0m0.120s

Не уверен, почему время perlне сильно изменилось, возможно, кодировка должна быть установлена ​​другим способом

Sundeep
источник
Я только добавил это к моему собственному решению. Я удалил его, когда увидел твои слова. :-)
Кусалананда
да, я спорил, чтобы удалить мой, прежде чем увидел ваше редактирование снова :)
Sundeep
Нет необходимости сортировать численно индексированный массив. Это всегда заказано с увеличивающимся индексом. (ну, по крайней мере, в awk :-))
Исаак
lengthбез ()прекрасно работает здесь, поэтому было бы излишним добавлять фигурные скобки. Я использую GNU awk, хотя.
Сергей Колодяжный
2
@SergiyKolodyazhnyy yup, руководство gnu awk говоритIn older versions of awk, the length() function could be called without any parentheses. Doing so is considered poor practice, although the 2008 POSIX standard explicitly allows it, to support historical practice. For programs to be maximally portable, always supply the parentheses
Sundeep
5

Вот perlэквивалент (с - необязательно - сортировка):

$ perl -lne '
    $h{length($_)}++ }{ for $n (sort keys %h) {print "$n character words - $h{$n}"}
' file
2 character words - 3
5 character words - 1
7 character words - 1
steeldriver
источник
Если индексы ключей являются числовыми: нужно ли сортировать массив ключей в Perl?
Исаак
1
@Arrow: в этом ответе используется хеш (т. Е. Ассоциативный массив со строковыми ключами), и у них неопределенный порядок ключей, так что да. На самом деле, ответ немного глючит, потому что он сортирует ключи как строки, а не числа. Добавление {$a<=>$b}после sortисправит это. В качестве альтернативы можно использовать обычный массив с числовыми ключами и просто пропустить любые ключи, где значение равно нулю / не определено.
Ильмари
@IlmariKaronen Спасибо, теперь лучше. Какая разница в фигурных скобках !!
Исаак
Было бы более эффективно использовать массив вместо хеша. Оператору требуется миллионы строк, поэтому любые затраты на проверку и пропуск нулей при печати легко компенсируются более дешевой индексацией.
Питер Кордес
5

Альтернативой один вызов GNU AWK, используя Printf :

$ awk 'BEGIN { PROCINFO["sorted_in"] = "@ind_str_asc"}
       {c[length($0)]++}
       END{
           for(i in c){printf("%s character words - %s\n",i,c[i])}
          }' infile
2 character words - 3
5 character words - 1
7 character words - 1

Основной алгоритм просто собирает количество символов в массиве. Конечная часть печатает собранные отсчеты в формате printf.

Быстро, просто, один звонок в awk.

Чтобы быть точным: немного больше памяти используется для хранения массива.
Но сортировка не вызывается (индексы числовых массивов устанавливаются так, чтобы их всегда обходили, сортируя вверх с помощью PROCINFO), и только одна внешняя программа: awkвместо нескольких.

Исаак
источник
1
for inможет случиться так, что индексы числовых массивов будут приведены в числовом порядке, по крайней мере, для некоторых значений или в некоторых реализациях awk, но это не обязательно, не традиционно, и определенно не универсально. Это часто случается для крошечных наборов, таких как 2 или 3 или, может быть, 4; Попробуйте 10 или 20 на каждом awk, к которому у вас есть доступ (без PROCINFO или WHINY_USERS в gawk), и я держу пари, что $ 50, по крайней мере, один случай не отсортирован.
dave_thompson_085
Спасибо за ваш вклад. Используя это : я считаю, что это отсортировано сейчас. :-)
Исаак
1
@ind_str_ascсортирует как строки, которые будут правильными для чисел, только если они все однозначные (как в вашем примере); используйте, @ind_num_ascесли (любые) значения могут быть 10 или больше. И хотя сейчас это не так важно, как раньше, эта функция только gawk 4.0 up .
dave_thompson_085