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

Файл /etc/magic или /usr/share/misc/magic имеет список последовательностей что команда file использование для определения типа файла.

Обратите внимание, что двоичный файл может просто быть решением для нейтрализации. Иногда файлы со странным кодированием считают двоичными также.

grep на Linux имеет некоторые опции обработать двоичные файлы как --binary-files или -U / --binary

95
30.04.2013, 13:08
18 ответов

Можно сделать это с sed и awk:

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

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

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

's/[^(]//g'

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

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

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

111
27.01.2020, 19:30
  • 1
    +1 должен быть более эффективным, чем tr&wc версия. А-ч –  Stéphane Gimenez 14.08.2011, 22:41
  • 2
    Да, но это может обработать Unicode? –  amphetamachine 15.08.2011, 13:51
  • 3
    @amphetamachine, да - по крайней мере, быстрый тест с ß (utf шестнадцатеричное число: c3 9f) (вместо ") работы как ожидалось, т.е. tr, sed и awk сделайте дополнение/замену/подсчет без проблемы - в системе Ubuntu 10.04. –  maxschlepzig 15.08.2011, 21:29
  • 4
    Большинство версий tr, включая TR GNU и классический TR Unix, воздействуйте на однобайтовые символы, и не совместимый Unicode.. Заключенный в кавычки из TR Википедии (Unix).. Попробуйте этот отрывок: echo "aā⧾c" | tr "ā⧾" b ... на Ubuntu 10.04... ß единственный байт Расширенный латинский символ и обрабатывается tr... Реальная проблема здесь не это tr не обрабатывает Unicode (потому что ВСЕ символы являются Unicode), это - действительно это tr только дескрипторы, однобайтовые за один раз.. –  Peter.O 15.08.2011, 22:32
  • 5
    @fred, нет, ß не является ни одним символом байта - его положение Unicode является U+00DF, который кодируется как 'c3 9f' в UTF-8, т.е. два байта. –  maxschlepzig 16.08.2011, 10:20

Вот другое решение 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
27.01.2020, 19:30
  • 1
    Это не сообщит относительно последней строки, если она не будет иметь запаздывания '\n' –  Peter.O 16.08.2011, 01:24
  • 2
    @fred, да, который является нарочно, потому что строка без запаздывания \n не реальная строка. Это - то же поведение как с моим другим sed/awk (tr/awk) ответ. –  maxschlepzig 16.08.2011, 10:25

Я решил записать причину программы 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
27.01.2020, 19:30
  • 1
    Спасибо! спасибо за то, что скучали так, чтобы я мог изучить что-то. О, ожидайте, Вам нужен возврат? –  StackExchange for All 15.08.2011, 02:31
  • 2
    *пожимает плечами*, если Вы хотите быть полностью корректными, также необходимо добавить еще много #includes, но предупреждения по умолчанию на моем компиляторе, кажется, не заботятся. –  user606723 15.08.2011, 02:39
  • 3
    Можно не учесть free(line) потому что выход из программы неявно освобождает всю выделенную память - затем существует место для a return 0; ... ;). Даже в примерах это не хороший стиль для отъезда кода возврата неопределенным. Btw, getline расширение GNU - в случае, если кто-то задается вопросом. –  maxschlepzig 15.08.2011, 09:04
  • 4
    @maxschlepzig: на память указывают с методической точностью выделенная getline ()? Это выделяется динамично на "куче" malloc или статически на стеке? Вы сказали, что освобождение его не необходимо, также - разве это не выделяется динамично? –  StackExchange for All 15.08.2011, 09:28
  • 5
    @Tim, да, например, если Вы осуществляете рефакторинг код, таким образом, что это - автономная функция - говорит -f, который несколько раз называют из другого кода, затем необходимо звонить free после последней возможности getline в конце этой функции f. –  maxschlepzig 15.08.2011, 10:44

Другая возможная реализация с awk и gsub:

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

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

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

8
27.01.2020, 19:30
  • 1
    Можно сохранить один символ, т.е. при удалении stdin перенаправления... ;) –  maxschlepzig 14.08.2011, 23:34
  • 2
    @maxschlepzig: да, конечно, ping ;) –  enzotib 14.08.2011, 23:43
  • 3
    awk '{print gsub(/"/,"")}' input-file был бы достаточно, как "Для каждой подстроки, соответствующей регулярному выражению r в строке t, заменил бы строкой s и возвратил бы количество замен". (человек awk), –  manatwork 06.09.2011, 15:42

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

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

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

11
27.01.2020, 19:30
  • 1
    Когда последняя строка не имеет запаздывания \n, выходы цикла с условием продолжения, потому что, хотя она считала последнюю строку, она также возвращает ненулевой код выхода для указания на EOF... для обхождения его, следующие работы отрывка (.. Это было, прослушивая меня некоторое время, и я только что обнаружил этот workaroung)... eof=false; IFS=; until $eof; do read -r || eof=true; echo "$REPLY"; done –  Peter.O 16.08.2011, 00:42
  • 2
    @Gilles: Вы добавили запаздывание / это не нужно в ударе. Это - ksh требование? –  enzotib 16.08.2011, 10:35
  • 3
    Запаздывание / необходим в более старых версиях ksh и IIRC в более старых версиях удара также. –  Gilles 'SO- stop being evil' 16.08.2011, 11:15

Используя 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
27.01.2020, 19:30

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

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

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

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

52
27.01.2020, 19:30
  • 1
    Возможно, отредактируйте свой ответ для использования кавычек одиночных игр вместо этого для выхода. Это будет работать с любым символом (кроме '). Кроме того, это имеет странное поведение с пустыми строками. –  Stéphane Gimenez 15.08.2011, 19:08
  • 2
    Вопрос конкретно использует " таким образом, я чувствую себя обязанным заставить код работать с ним. Это зависит, что окружает Вас, используют погоду, символа нужно оставить, но bash/tcsh должен будет оба выйти " –  Martin York 15.08.2011, 19:10
  • 3
    Конечно, но нет никакой проблемы с -F'"'. –  Stéphane Gimenez 15.08.2011, 19:12
  • 4
    +1, Что хорошая идея использовать FS.... Это разрешит пустую строку, показывающую-1, и, например, "1$" от командной строки удара... awk -F"$1" '{print NF==0?NF:NF-1}' filename –  Peter.O 16.08.2011, 01:19
  • 5
    Также работа с несколькими символами как разделитель... полезный! –  COil 30.09.2016, 18:35

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

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

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

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

expr length "${x//[^\"]/}"
2
27.01.2020, 19:30
  • 1
    Обратите внимание, что это характерно для GNU expr и байты количеств, не символы. С другим expr: expr "x${x...}" : "x.*" - 1 –  Stéphane Chazelas 23.11.2014, 23:27
  • 2
    О, право, Спасибо! я изменил его с помощью другой идеи, которую я просто имел, который имеет преимущество не использования внешней программы вообще. –  Marian 05.03.2015, 01:08

Ответы, использующие 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
27.01.2020, 19:30

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

awk '{print gsub(/"/, "")}'
9
27.01.2020, 19:30

Сначала вы должны знать, что является допустимой переменной в awk . POSIX определил переменную awk как

Операнд, начинающийся с < подчеркивание > или алфавитного символа из портативного набора символов (см. таблицу в XBD Portable набор символов), за которым следует последовательность знаков подчеркивания, цифр и алфавит из переносимого набора символов, за которым следует символ «» = символ, указывает назначение переменной, а не путь

Таким образом, если у вас есть переменная awk x , и вы хотите использовать x , просто сослаться на нее, написав x напрямую, например напечатать x или y = x .

При использовании $ x осуществляется доступ к переменным awk Field . В awk доступ к переменным поля осуществляется с помощью $ , за которым следует число или числовое выражение. Поэтому при написании $ x awk сначала вычислит x . Если х имел числовое значение, например 1 , оператор становится $1 , awk даст значение первой переменной поля. Или у вас есть числовое выражение, как (1 + 1) , то $ (1 + 1) стать $2 , вы получите значение второй переменной поля.

Обратите внимание, что, когда выражение номера поля вычисляется как что-либо, кроме неотрицательного целого числа , поведение является неопределенным (например, x = «qwerty» или x = «qwerty» + 1 , то доступ к $ x не указан):

Эффект вычисления выражения номера поля на что-либо другое чем неотрицательное целое число не указано; неинициализированные переменные или Последовательности значения не должны преобразовываться в числовые значения в данном контексте

В вашем случае реализация awk имела выражение номера поля оценки (которое является последовательностью) в 0 , поэтому вы получили значение переменной $0 . В другой реализации awk результат может быть другим (по крайней мере, в OpenBSD awk вы получите ошибку недопустимое поле ).

-121--96062-

Попробуйте sudo umount -v/run/media/Harry/2030-0761 . Если написано «устройство занято», попробуйте lsof/run/media/Harry/2030-0761 ; будут показаны приложения, у которых что-то в файловой системе открыто. Скорее всего, это будут оболочки и файловые браузеры. Проверьте их и выйдите из файловой системы и повторите попытку umount .

Если это не работает, так как вы сказали, что намереваетесь переформатировать это в любом случае, просто вытащите его, отключите автоматизацию , положите его обратно и делайте, что хотите.

-121--175323-

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

Для подсчета определенного символа.

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

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

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

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

3
27.01.2020, 19:30

Может быть, более прямой, чисто AWK-ответ будет использовать разделение. Сплит принимает строку и превращает его в массив, возвращаемое значение - это количество элементов массива, создаваемых элементами + 1.

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

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

Подробнее о разделении http://www.staff.science.uu.nl/~ooStr102/docs/nawk/nawk_92.html

3
27.01.2020, 19:30

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

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

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

6
27.01.2020, 19:30

У меня была такая же проблема, но для меня решение было другим. Мой пользователь не был настроен для использования Bash в качестве оболочки, он использовал ZSH в качестве оболочки вместо этого, поэтому файлы Bash Dot не работали при входе в систему. Open / etc / passwd с текстовым редактором и ищите ваше имя пользователя и какую оболочку он использует:

root:x:0:0:root:/root:/bin/zsh

Так выглядит моя запись пользователя. Обратите внимание, что он говорит / bin / zsh вместо / bin / bash. Для ZSH правильный точечный файл ~ / .zprofile. Его содержимое будет запускать каждый раз, когда вы войти в систему, используя SSH.

-121--38103-

Заменить A подсчетом . Вывод - это счетчик для каждой строки.

perl -nE 'say y!a!!'
2
27.01.2020, 19:30

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

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

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

2
27.01.2020, 19:30

Maxsessions Maxsessions Для более высокого значения (по умолчанию: 10) в SSHD_CONFIG (5) и перезапуска SSHD .

Вы, вероятно, захотите настроить MaxStartups . Страница человека все объясняет.

-121--249901-

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

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

Я использую как Введите португальский перевод Victor Hugo "Les stageables" (отличная книга!) И подсчитайте вхождения «А». Мое издание имеет 5 томов, множество страниц ...

$ wc miseraveis.txt 
29331  304166 1852674 miseraveis.txt 

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

Каждый ответ был запущен 3 раза и выбрать лучшее.

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

  • 14 из 16 примерных решений потребовалось менее 1s; 9 Менее 0,1с, многие из них используют трубы
  • 2 решения, используя линию Bash по линии, обработанные линии 30K путем создания новых процессов, Рассчитайте правильное решение в 10-х / 20.
  • GreeP -OP A Tree Times быстрее тогда GreeP -O A (10; 11 против 12)
  • Разница между C и другими не такаями, как я ожидал. (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
27.01.2020, 19:30
grep -n -o \" file | sort -n | uniq -c | cut -d : -f 1

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

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

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

1
27.01.2020, 19:30

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

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

$ a='"Hello!"'

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

$ b="${a//[^\"]}"; echo "${#b}"
2
1
27.01.2020, 19:30

Теги

Похожие вопросы