it-swarm-ru.tech

Как посчитать количество конкретного символа в каждой строке?

Мне было интересно, как подсчитать количество конкретного символа в каждой строке некоторыми утилитами обработки текста?

Например, чтобы посчитать " в каждой строке следующего текста

"hello!" 
Thank you!

Первая строка имеет две, а вторая строка имеет 0.

Другой пример - считать ( в каждой строке.

97
Tim

Вы можете сделать это с помощью sed и ​​awk:

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

Где dat - ваш пример текста, sed удаляет (для каждой строки) все не -" символы, а awk печатает для каждой строки ее размер (т. Е. length is эквивалентно length($0), где $0 обозначает текущую строку).

Для другого персонажа вам просто нужно изменить выражение sed. Например, для (:

's/[^(]//g'

pdate:sed является излишним для задачи - достаточно tr. Эквивалентное решение с tr:

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

Это означает, что tr удаляет все символы, которые не являются (-c означает дополнение) в наборе символов "\n.

115
maxschlepzig

Я бы просто использовал awk

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

Здесь мы устанавливаем разделитель полей (с флагом -F) как символ " тогда все, что мы делаем, это печатаем количество полей NF - 1. Количество вхождений целевого символа будет на единицу меньше, чем количество разделенных полей.

Для забавных персонажей, которые интерпретируются командной оболочкой, вам просто нужно избегать их, иначе командная строка попытается их интерпретировать. Так что для обоих " а также ) вам нужно выйти из разделителя полей (с помощью \).

52
Martin York

Используя tr ard wc:

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

Применение:

$ 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
Stéphane Gimenez

Еще одна реализация, которая не зависит от внешних программ, в bash, zsh, yash и ​​некоторых реализациях/версиях ksh:

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

Используйте line="${line//[!(]}" для подсчета (.

11
enzotib

Ответы, использующие awk, терпят неудачу, если количество совпадений слишком велико (что в моей ситуации). Для ответа от loki-astari сообщается следующая ошибка:

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

Для ответа из enzotib (и эквивалента из manatwork ) возникает ошибка сегментации:

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

Решение sed с помощью maxschlepzig работает правильно, но медленно (время указано ниже).

Некоторые решения еще не предложены здесь. Во-первых, используя grep:

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

И используя Perl:

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

Вот некоторые моменты времени для нескольких решений (упорядочены от самых медленных до самых быстрых); Я ограничил вещи одной строкой здесь. "foo.txt" - это файл с одной строкой и одной длинной строкой, содержащий 84922 совпадений.

## 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

Другое awk решение:

awk '{print gsub(/"/, "")}'
9
Stéphane Chazelas

Еще одна возможная реализация с awk и gsub:

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

Функция gsub является эквивалентом sed _'s///g'_.

Используйте gsub("[^(]", "") для подсчета _(_.

8
enzotib

Я решил написать программу на C, потому что мне было скучно.

Вы, вероятно, должны добавить проверку ввода, но кроме этого все готово.

#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

Для строки самое простое будет с tr и ​​wc (нет необходимости перебивать с помощью awk или sed) - но обратите внимание на приведенные выше комментарии о tr, считает байты, а не символы -

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

где $x - это переменная, которая содержит строку (не файл) для оценки.

6
Ocumo

Вот еще одно решение C, которому требуется только STD C и меньше памяти:

#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

Возможно, более прямым, чисто awk-ответом будет использование split. Split берет строку и превращает ее в массив, возвращаемое значение - количество сгенерированных элементов массива + 1.

Следующий код распечатает количество раз "появляется в каждой строке.

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

больше информации о сплите http://www.staff.science.uu.nl/~oostr102/docs/nawk/nawk_92.html

3
bleurp

Мы можем использовать grep с regex, чтобы сделать его более простым и мощным.

Посчитать конкретный персонаж.

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

Для подсчета специальных символов, включая пробельные символы.

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

Здесь мы выбираем любой символ с [\S\s] и с -o параметр, который мы делаем grep, чтобы печатать каждое совпадение (то есть каждый символ) в отдельной строке. А затем используйте wc -l для подсчета каждой строки.

3
Kannan Mohan

Для чистого решения bash (однако, оно зависит от bash): Если $x - это переменная, содержащая вашу строку:

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

${x// вещь удаляет все символы кроме ", ${#x2} вычисляет длину этого отдыха.

(Исходное предложение с использованием expr, которое имеет проблемы, см. Комментарии:)

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

Вот простой скрипт Python для определения количества " в каждой строке файла:

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

Здесь мы использовали метод count встроенного типа str.

2
heemayl

Замените a на символ, который нужно посчитать. Выходной счетчик для каждой строки.

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

Сравнение времени представленных решений (не ответ)

Эффективность ответов не важна. Тем не менее, следуя подходу @josephwb, я постарался найти ответы на все вопросы.

Я использую в качестве ввода португальский перевод Виктора Гюго "Les Miserables" (великая книга!) И подсчитываю вхождения "a". Мое издание имеет 5 томов, много страниц ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

C ответы были скомпилированы с GCC, (без оптимизации).

Каждый ответ запускался 3 раза и выбирался лучший.

Не доверяйте этим цифрам слишком сильно (моя машина выполняет другие задачи и т.д. И т.д.). Я делюсь этим временем с вами, потому что я получил некоторые неожиданные результаты, и я уверен, что вы найдете еще немного ...

  • 14 из 16 временных решений заняли менее 1 с; 9 менее 0,1 с, многие из них используют трубы
  • 2 решения, используя bash построчно, обрабатывали 30 тыс. Строк, создавая новые процессы, вычисляя правильное решение в 10 с/20 с.
  • grep -oP a в три раза быстрее, чем grep -o a (10; 11 против 12)
  • Разница между С и другими не так велика, как я ожидал. (7; 8 против 2; 3)
  • (выводы приветствуются)

(результаты в случайном порядке)

=========================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

где grep выполняет всю тяжелую работу: сообщает о каждом символе, найденном в каждом номере строки. Остальное просто сложить счетчик на строку и отформатировать вывод.

Удалить -n и ​​получите счетчик для всего файла.

Подсчет 1,5Meg текстового файла менее чем за 0,015 секунды кажется быстрым.
И работает с символами (не байтами).

1
user79743

Решение для Баш. Внешняя программа не вызывается (быстрее для коротких строк).

Если значение находится в переменной:

$ a='"Hello!"'

Это напечатает, сколько " это содержит:

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