Рубиновый эквивалент grep -C 5, чтобы получить контекст строк вокруг совпадения?

Я немного искал это, но я, должно быть, использую неправильные термины - есть ли у ruby ​​способ поиска строки/регулярного выражения, а также возврата окружающих 5 строк (выше и ниже)? Я знаю, что мог бы просто вызвать "grep -C 5 ..." или даже написать свой собственный метод, но похоже, что-то, что есть в ruby, и я просто не использую правильные условия поиска.


person wonderfulthunk    schedule 03.05.2010    source источник


Ответы (3)


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

s = %{The first line
The second line
The third line
The fourth line
The fifth line
The sixth line
The seventh line
The eight line
The ninth line
The tenth line
}

EOL для меня "\n", но для вас это может быть "\r\n". Я вставлю это в константу:

EOL = '\n'

Чтобы упростить регулярное выражение, мы определим шаблон для «контекста» только один раз:

CONTEXT_LINES = 2
CONTEXT = "((?:.*#{EOL}){#{CONTEXT_LINES}})"

И мы будем искать любую строку, содержащую слово «пятый». Обратите внимание, что это регулярное выражение должно захватывать всю строку, включая конец строки, чтобы оно работало:

regexp = /.*fifth.*#{EOL}/

Наконец, выполните поиск и покажите результаты:

s =~ /^#{CONTEXT}(#{regexp})#{CONTEXT}/
before, match, after = $1, $2, $3
p before    # => "The third line\nThe fourth line\n"
p match     # => "The fifth line\n"
p after     # => "The sixth line\nThe seventh line\n"
person Wayne Conrad    schedule 03.05.2010
comment
Это было здорово, спасибо! Это работает как шарм - мне пришлось вернуться и перечитать, что конец строки должен быть включен в регулярное выражение, которое я ищу, я пропустил это, когда впервые попробовал это. - person wonderfulthunk; 04.05.2010

Спасибо за контекстный grep. Я подумал, что мог бы добавить, что, когда совпадение приближается к верхней или нижней части, и вам все еще нужны все строки, которые вы можете получить, даже без всех доступных строк CONTEXT_LINES, вы можете изменить определение CONTEXT следующим образом:

CONTEXT = "((?:.*#{EOL}){0,#{CONTEXT_LINES}})"

По умолчанию совпадения являются жадными, поэтому, если часть или все строки CONTEXT_LINES доступны, это то, что вы захватите.

person Richard Ryan    schedule 03.09.2013

Я не думаю, что вы можете предоставить аргументы для grep; на основе API.

Вы всегда можете написать метод. Что-то в этом роде:

def new_grep(enum, pattern, lines)
 values = enum.grep(/pattern/).map do |x| 
   index = enum.index(x)
   i = (index - lines < 0) ? 0 : index - lines
   j = (index + lines >= enum.length) ? enum.length-1 : index + lines 
   enum[i..j]
 end
 return values.flatten.uniq
end
person Pran    schedule 03.05.2010
comment
Это тоже может сработать, но я обнаружил, что ответ Уэйна больше соответствует тому, что я хотел. - person wonderfulthunk; 04.05.2010