Мне было интересно, как подсчитать количество конкретного символа в каждой строке некоторыми утилитами обработки текста?
Например, чтобы посчитать "
в каждой строке следующего текста
"hello!"
Thank you!
Первая строка имеет две, а вторая строка имеет 0.
Другой пример - считать (
в каждой строке.
Ответы:
Вы можете сделать это с помощью
sed
иawk
:Где
dat
ваш пример текста, sed удаляет (для каждой строки) все не-"
символы иawk
печатает для каждой строки ее размер (тоlength
есть эквивалентноlength($0)
, где$0
обозначает текущую строку).Для другого персонажа вам просто нужно изменить выражение sed. Например,
(
чтобы:Обновление:
sed
является своего рода излишним для задачи -tr
достаточно. Эквивалентное решение сtr
:Это означает, что
tr
удаляет все символы, которые не являются (-c
означает дополнение) в наборе символов"\n
.источник
tr
&wc
версия.ß
(UTF HEX: c3 9F) (вместо"
) работает , как ожидалось, то естьtr
,sed
иawk
сделать дополнение / замену / подсчета без проблем - на 10,04 системе Ubuntu.tr
, включая GNU tr и классический Unix tr, работают с однобайтовыми символами и не совместимы с Unicode .. Цитируется из Википедии tr (Unix) .. Попробуйте этот фрагмент:echo "aā⧾c" | tr "ā⧾" b
... в Ubuntu 10.04 ...ß
это однобайтовый Расширенный латинский символ и обрабатываетсяtr
... Реальная проблема здесь не в том,tr
что не обрабатывается Unicode (потому что ВСЕ символы Unicode), а в том, что онtr
обрабатывает только один байт за раз ..Я бы просто использовал awk
Здесь мы устанавливаем разделитель полей (с флагом -F) в качестве символа,
"
тогда все, что мы делаем, это печатаем количество полейNF
- 1. Количество вхождений целевого символа будет на один меньше, чем количество разделенных полей.Для забавных символов, которые интерпретируются оболочкой, вам просто нужно убедиться, что вы их избегаете, иначе командная строка попытается их интерпретировать. Так что для обоих
"
и)
вам нужно экранировать разделитель полей (с\
).источник
'
). Кроме того, у него странное поведение с пустыми строками."
поэтому я чувствую себя обязанным заставить код работать с ним. Это зависит от того, какую оболочку вы используете, если от персонажа требуется экранирование, но и bash / tcsh должны будут сбежать »-F'"'
.awk -F"$1" '{print NF==0?NF:NF-1}' filename
Используя
tr
ardwc
:Использование:
источник
tr
не обрабатывает символы, которые используют более одного байта .. см. википедию tr (Unix) .. т.е.tr
не соответствует Unicode.$IFS
, иначеread
обрежете их с начала и до конца.echo
для произвольных данныхtr
реализации поддерживают многобайтовые символы, но вwc -c
любом случае они учитывают байты, а не символы (нужныwc -m
символы).Еще одна реализация , которая не зависит от внешних программ, в
bash
,zsh
,yash
и некоторые реализации / версииksh
:Используйте
line="${line//[!(]}"
для подсчета(
.источник
eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done
/
который не нужен в bash. Это требование кш?/
необходим в старых версиях ksh и IIRC в старых версиях bash.Использование ответов
awk
не удается, если количество совпадений слишком велико (что в моей ситуации). Для ответа от loki-astari сообщается о следующей ошибке:Для ответа от enzotib (и эквивалента от manatwork ) возникает ошибка сегментации:
sed
Решение по maxschlepzig работает правильно, но медленно (тайминги ниже).Некоторые решения еще не предложены здесь. Во-первых, используя
grep
:И с помощью
perl
:Вот некоторые моменты времени для нескольких решений (упорядочены от самых медленных до самых быстрых); Я ограничил вещи однострочником здесь. «foo.txt» - это файл с одной строкой и одной длинной строкой, содержащий 84922 совпадений.
источник
Другое
awk
решение:источник
Еще одна возможная реализация с awk и gsub:
Функция
gsub
является эквивалентом sed's///g'
.Используйте
gsub("[^(]", "")
для подсчета(
.источник
awk '{print gsub(/"/,"")}' input-file
будет достаточно, так как «Для каждой подстроки, совпадающей с регулярным выражением r в строке t, подставьте строку s и верните число подстановок». (man awk)Я решил написать программу на C, потому что мне было скучно.
Вы, вероятно, должны добавить проверку ввода, но кроме этого все готово.
источник
free(line)
потому что выход из программы неявно освобождает всю выделенную память - тогда есть место дляreturn 0;
...;). Даже в примерах не рекомендуется оставлять код возврата неопределенным. Кстати,getline
расширение GNU - на случай, если кому-то интересно.f
которая вызывается несколько раз из другого кода, то вам придется вызыватьfree
после последнего вызоваgetline
в конце этой функцииf
.Для строки самое простое было бы с
tr
иwc
(не нужно перебивать с помощьюawk
илиsed
) - но обратите внимание на приведенные выше комментарии оtr
количестве байтов, а не символов -где
$x
переменная, которая содержит строку (не файл) для оценки.источник
Вот еще одно решение C, которому требуется только STD C и меньше памяти:
источник
\n
является реальной. Это то же поведение, что и в моем другом ответе sed / awk (tr / awk).Мы можем использовать
grep
с,regex
чтобы сделать его более простым и мощным.Посчитать конкретный персонаж.
Для подсчета специальных символов, включая пробельные символы.
Здесь мы выбираем любой символ с
[\S\s]
и с-o
опцией, которую мы делаем,grep
чтобы напечатать каждое совпадение (то есть, каждый символ) в отдельной строке. А затем используйтеwc -l
для подсчета каждой строки.источник
"
в каждой строке; и для любых других символов. увидеть его вопрос, а также принял ответ.Возможно, более прямым, чисто awk-ответом будет использование split. Split берет строку и превращает ее в массив, возвращаемое значение - количество сгенерированных элементов массива + 1.
Следующий код распечатает количество раз "появляется в каждой строке.
больше информации о разделении http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html
источник
Вот простой скрипт на Python для определения количества
"
в каждой строке файла:Здесь мы использовали
count
метод встроенногоstr
типа.источник
Для чисто решения bash (однако, оно
$x
зависит от bash): If - это переменная, содержащая вашу строку:Эта
${x//
вещь удаляет все символы, кроме"
,${#x2}
рассчитывает длину этого отдыха.(Оригинальное предложение, при использовании
expr
которого возникают проблемы, см. В комментариях:)источник
expr
и учитывает байты, а не символы. С другимиexpr
:expr "x${x...}" : "x.*" - 1
Заменить
a
на символ, который будет засчитан. Выходной счетчик для каждой строки.источник
Сравнение времени представленных решений (не ответ)
Эффективность ответов не важна. Тем не менее, следуя подходу @josephwb, я постарался найти ответы на все вопросы.
Я использую в качестве ввода португальский перевод Виктора Гюго "Les Miserables" (великая книга!) И подсчитываю вхождения "a". Мое издание имеет 5 томов, много страниц ...
C ответы были скомпилированы с GCC, (без оптимизации).
Каждый ответ запускался 3 раза и выбирался лучший.
Не доверяйте этим цифрам слишком сильно (моя машина выполняет другие задачи и т. Д. И т. Д.). Я делюсь этим временем с вами, потому что я получил некоторые неожиданные результаты, и я уверен, что вы найдете еще немного ...
grep -oP a
в три раза быстрееgrep -o a
(10; 11 против 12)(результаты в случайном порядке)
источник
где grep выполняет всю тяжелую работу: сообщает о каждом символе, найденном в каждом номере строки. Остальное просто сложить счетчик на строку и отформатировать вывод.
Удалите
-n
и получите счет для всего файла.Подсчет 1,5Meg текстового файла менее чем за 0,015 секунды кажется быстрым.
И работает с символами (не байтами).
источник
Решение для Баш. Внешняя программа не вызывается (быстрее для коротких строк).
Если значение находится в переменной:
Это напечатает, сколько
"
он содержит:источник