Как я могу получить строки, где определенное слово повторяется ровно N раз?

8

Для этого дан вход:

How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this

Я хочу этот вывод:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Получение целых строк содержит только три повторяющихся слова «это». (совпадение без учета регистра)

αғsнιη
источник
4
Слишком широкому избирателю: как вопрос может быть более конкретным?
Джейкоб Влейм
@JacobVlijm В этом есть «слишком много возможных ответов». Пик $RANDOM_LANGUAGE- кто-нибудь сможет найти решение в этом.
Муру
@muru Я бы сказал наоборот, если ограничить его одним языком, то это станет вопросом программирования (языка). Теперь это проблемный вопрос. Возможных решений (языков) может быть много, но не так много очевидных.
Джейкоб Влейм

Ответы:

13

В perl, заменить thisна себя без учета регистра и посчитать количество замен:

$ perl -ne 's/(this)/$1/ig == 3 && print' <<EOF
How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this
EOF
How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Вместо этого используется количество совпадений :

perl -ne 'my $c = () = /this/ig; $c == 3 && print'

Если у вас есть GNU awk, очень простой способ:

gawk -F'this' -v IGNORECASE=1 'NF == 4'

Количество полей будет на один больше количества разделителей.

Мур
источник
Зачем заменять? мы не можем посчитать это напрямую без замены?
αғsнιη
Действительно, мы можем посчитать, код немного длиннее: stackoverflow.com/questions/9538542/…
muru
Upvote для команды gawk.
Шри
9

Предполагая, что ваш исходный файл - tmp.txt,

grep -iv '.*this.*this.*this.*this' tmp.txt | grep -i '.*this.*this.*this.*'

Левый grep выводит все строки, которые не имеют 4 или более регистрозависимых вхождений «this» в tmp.txt.

Результат передается в правый grep, который выводит все строки с 3 или более вхождениями в левом результате grep.

Обновление: благодаря @Muru, вот лучшая версия этого решения,

grep -Eiv '(.*this){4,}' tmp.txt | grep -Ei '(.*this){3}'

замените 4 на n + 1 и 3 на n.

Шри
источник
Это потерпит неудачу при N> 4. И первое grepдолжно закончиться *.
PS95
1
Я имею в виду, что вы не можете написать это для N = 50. И вопрос точно для трех, поэтому вам нужен еще один grep, который отбрасывает все выходные данные, содержащие меньше или равные двум this. grep -iv '.*this.*this.*this.*this.*' tmp.txt | grep -i '.*this.*this.*this.* |grep -iv '.*this.*this.'
PS95
@ prakharsingh95 Это не удалось при n> 4 и * не требуется в первом grep.
Шри
1
@KasiyA что вы думаете о моем ответе?
Шри
5
Упростим это немного: grep -Eiv '(.*this){4,}' | grep -Ei '(.*this){3}'- это может сделать его практичным для N = 50.
Муру
9

В Python это сделало бы работу:

#!/usr/bin/env python3

s = """How to get This line that this word repeated 3 times in THIS line?
But not this line which is THIS word repeated 2 times.
And I will get This line with this here and This one
A test line with four this and This another THIS and last this"""

for line in s.splitlines():
    if line.lower().count("this") == 3:
        print(line)

выходы:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Или для чтения из файла с файлом в качестве аргумента:

#!/usr/bin/env python3
import sys

file = sys.argv[1]

with open(file) as src:
    lines = [line.strip() for line in src.readlines()]

for line in lines:
    if line.lower().count("this") == 3:
        print(line)
  • Вставьте скрипт в пустой файл, сохраните его как find_3.py, запустите его командой:

    python3 /path/to/find_3.py <file_withlines>
    

Конечно, слово «это» может быть заменено любым другим словом (или другой строкой или разделом строки), а число вхождений в строке может быть установлено на любое другое значение в строке:

    if line.lower().count("this") == 3:

редактировать

Если файл будет большим (сотни тысяч / миллионы строк), приведенный ниже код будет быстрее; он читает файл в строке вместо того, чтобы загружать файл сразу:

#!/usr/bin/env python3
import sys
file = sys.argv[1]

with open(file) as src:
    for line in src:
        if line.lower().count("this") == 3:
            print(line.strip())
Якоб Влейм
источник
Я не эксперт по Python, как я могу читать из файла? спасибо
αғsнιη
1
@KasiyA отредактировано, чтобы использовать файл в качестве аргумента.
Джейкоб Влейм
Просто любопытно: почему вы не использовали генератор во втором фрагменте кода?
Муру
6

Вы можете поиграть немного с awkэтим:

awk -F"this" 'BEGIN{IGNORECASE=1} NF==4' file

Это возвращает:

How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

объяснение

  • Что мы делаем, так это определяем разделитель полей для thisсебя. Таким образом, в строке будет столько полей +1, сколько раз thisпоявится слово .

  • Чтобы сделать его нечувствительным к регистру, мы используем IGNORECASE = 1. См. Ссылку: Чувствительность к регистру при сопоставлении .

  • Тогда нужно просто сказать, NF==4чтобы все эти строки были thisровно трижды. Больше кода не требуется, поскольку {print $0}(то есть печать текущей строки) - это поведение по умолчанию, awkкогда выражение вычисляется в True.

fedorqui
источник
Уже выложено , но хорошее объяснение.
Муру
@ Муру, о, я этого не видел! Мои извинения и +1 для вас.
Федорки
5

Предполагая, что строки хранятся в файле с именем FILE:

while read line; do 
    if [ $(grep -oi "this" <<< "$line" | wc -w)  = 3 ]; then 
        echo "$line"; 
    fi  
done  <FILE
PS95
источник
1
Спасибо, вы можете удалить свою sed ...команду и добавить -oопцию grep -oi ...вместо.
αғsнιη
Проще:$(grep -ic "this" <<<"$line")
Муру,
2
@muru Нет, -cопция будет подсчитывать количество строк, которые соответствуют «this», а не количеству «this» слов в каждой строке.
αғsнιη
1
@KasiyA Ах, да. Виноват.
Муру
@KasiyA, не будет -lи -wбыть эквивалентен в этом случае?
PS95
4

Если вы в Vim:

g/./if len(split(getline('.'), 'this\c', 1)) == 4 | print | endif

Это будет просто печатать согласованные линии.

Бор
источник
Хороший пример для поиска строк с n вхождениями слова при использовании Vim.
Шри
0

Рубиновое однострочное решение:

$ ruby -ne 'print $_ if $_.chomp.downcase.scan(/this/).count == 3' < input.txt                                    
How to get This line that this word repeated 3 times in THIS line?
And I will get This line with this here and This one

Работает довольно просто: мы перенаправляем файл в stdin ruby, ruby ​​получает строку из stdin, очищает ее с помощью chompи downcaseи scan().countдает нам количество вхождений подстроки.

Сергей Колодяжный
источник