1

Я хочу найти определенную строку по нескольким строкам в файле и получить строку, в которой найдено совпадение.

Однако моя проблема в том, что файл содержит одну очень длинную строку, а не слова, и я хочу найти подпоследовательность этой длинной строки. Следовательно, я не могу использовать pcregrep и просто искать word1\nword2. Поскольку я действительно хочу получить номер строки, в которой найдено совпадение, я не могу просто удалить все символы новой строки ...

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

Строка для поиска:

gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedcbaedhacebeeebcechbcbfeeccbdhcbfg

Файл для поиска в:

abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***GCBCDBFCEEBCFHFCHAACCDGFCEGFFGEDFFAEAEDC
BAEDHACEBEEEBCECHBCBFEECCBDHCBFG***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba

У кого-нибудь из вас есть простое решение для этого?

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

РЕДАКТИРОВАТЬ:

Большое спасибо за ваши ответы, они работают очень хорошо, если известна позиция символа новой строки.

Тем не менее, я прошу прощения за неточность в моем вопросе. Моя проблема в том, что я не знаю, есть ли новая строка, или даже более одной новой строки, внутри строки в файле и, более того, я не знаю, где она находится. Я исправил строку поиска, удалив новую строку, которую я невольно вставил.

Есть ли способ разрешить перевод строки в любую позицию строки?

3 ответа3

4

Я бы сделал это с помощью сценария sed . Поместите это в файл, затем используйте sed -nf для его запуска.

:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
    #   Found the first part, now discard it
    s/^.*$//
    #   Read a new line into the buffer
    N
    #   Discard the new line inserted by the N operation
    s/^\n//
    #   If next line isn't a match, start over
    /^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
    #   If it is a match, print the line number
    =
    }

Вот как это выглядит для запуска под bash . Обратите внимание, что он печатает номер строки второй совпадающей строки.

bash-4.1$ cat sample.txt
abcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcdeabcde
abcdeabcde***gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa
fbaaechaabdbffbebecebaacfcfcdcggfchddcefbcbdegbbba
bash-4.1$
bash-4.1$ cat findmatch.sed
:restart
/gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc$/{
   #  Found the first part, now discard it
   s/^.*$//
   #  Read a new line into the buffer
   N
   #  Discard the new line inserted by the N operation
   s/^\n//
   #  If next line isn't a match, start over
   /^baedhacebeeebcechbcbfeeccbdhcbfg/!b restart
   #  If it is a match, print the line number
   =
   }
bash-4.1$
bash-4.1$ sed -nf findmatch.sed sample.txt
3
bash-4.1$
3

Я немного сбит с толку относительно того, с какими ограничениями вы работаете. Тем не менее, если вам нужен номер строки, и grep, и pcregrep могут предоставить его вам под флагом -n.

$ pcregrep -nM "gcbcdbfceebcfhfchaaccdgfcegffgedffaeaedc\nbaedhacebeeebcechbcbfeeccbdhcbfg" | cut -d: -f1
2
baedhacebeeebcechbcbfeeccbdhcbfg***ggfbhbgcedabceedfa

pcregrep показывает только номер первой совпадающей строки, по-видимому, поэтому вам придется пропустить все остальные строки вывода с помощью sed (передайте вышеприведенное значение в sed -n 'p;N'), если вы хотите, чтобы в качестве выходных данных использовались только номера строк. ,

1

Теперь я добавляю новый ответ, чтобы лучше понять проблему. Я публикую это только как рабочий пример, но я не утверждаю, что это хороший пример. :)

Кроме того, я понимаю, что вопрос, похоже, не хотел использовать Python из-за некоторых опасений по поводу неэффективности. Поэтому я понимаю, что этот подход не выполняет весь запрос. :(

#!/usr/bin/env python
import sys

def findall_iter(S, pat):
  index = -1
  while True:
    try:
      index = S.index(pat, index+1)
      yield index
    except ValueError:
      raise StopIteration

def findall(S, pat):
  return list(findall_iter(S, pat))

# read in arguments
S = open(sys.argv[2]).read()
pattern = sys.argv[1]

# get indices of all newlines
newline_indices = findall(S, '\n')

# get psudo-indices of all pattern matches
pat_indices = findall(S.replace('\n', ''), pattern)

# iterate through each pattern match psudo-index and
# correlate it back to a real line number from the file
line_numbers = []
for pi in pat_indices:
  for i, ni in enumerate(newline_indices):
    if ni > pi+i:
      line = i + 1
      if line not in line_numbers:
        line_numbers.append(i+1)
      break

print '\n'.join(map(str, line_numbers))

Плюсы:

  • Если файл не слишком большой (<1 ГБ), все операции выполняются в памяти.
  • Использует метод str.index для поиска подстрок вместо (более медленного) сопоставления регулярных выражений
  • Понятнее, чем использование регулярных выражений

Минусы:

  • Не будет хорошо работать с большими файлами.
  • Создает две временные строки для выполнения работы.
  • Последний цикл for трудно понять.
  • Есть Python (который я лично не считаю мошенником).

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .