Страницы

Поиск по вопросам

суббота, 13 октября 2018 г.

поиск позиции многострочного текста

имеется большой текстовый файл1. например:
a b c d e
имеется другой текстовый файл2 с несколькими строками. например:
b c d
как с помощью posix-утилит (или хотя бы gnu-утилит, а в крайнем случае — на максимально платформо-неспецифичном си) найти номер строки в первом файле, начиная с которой эти файлы совпадают? для приведённого примера — это будет 2 (начиная со второй строки в первом файле содержатся точно такие же строки, как и во втором).

на данный момент нашёл лишь способ узнать, входит ли второй файл в первый:
$ grep -qzP "$(sed ':a;N;$!ba;s/
/\
/g' файл2)" файл1 && echo входит
но он не позволяет узнать номер строки, с которой началось совпадение.
пояснение по поводу программы для sed: она заменяет каждый перевод строки в файле2 на два символа
(обратный слэш и n), чтобы получилось регулярное выражение для grep-а. позаимстовавана отсюда: How can I replace a newline (
) using sed?


Ответ

C помощью patch
if line=$(diff -U0 файл2 /dev/null | patch -f --dry-run файл1 - | sed -rn 's/^Hunk #1 succeeded at ([0-9]+) .*/\1/p; /FAILED/ q1') then echo строка ${line:-1} else echo фрагмент не найден fi
Создаём патч удаляющий из файла-образца все строки и пытаемся его применить ко второму файлу в режиме проверки (dry-run). Если это возможно, patch сообщает найденное смещение, если оно не нулевое. Если нет - сообщает об ошибке.
С помощью diff.
Это был мой первый вариант, оставил его на всякий случай. Вроде работает, но как поведёт себя с очень большими файлами, не знаю:
diff -U0 файл2 файл1 | sed -rn '1!{/^-/q1}; 3{s/@@ -0,0 \+(1,([0-9]+)|(1)).*/\2\3/p}; /^@@ -[1-9]/ {G; h; /@@
@@/ q1 }'
Только строки нумеруются с 0.
Возможно будет неоправданно долго работать с большим файлом, так как diff будет выводить весь файл1 несовпадающий с файл2
Если совпадений нет и при частичном совпадении возвращает статус 1, текст при этом стоит игнорировать. Если файлы совпадают с самого начала, ничего не выводится, статус=0.
Пояснения по программе.
diff -U0 выдаёт патч в унифицированном формате без контекстных строк.
Первая группа отлавливает строки начинающиеся с минуса, кроме первой строки. Если такие строки присутствуют, значит diff не нашёл какой-то строки из файл2, программа завершается с кодом ошибки 1.
Вторая группа отлавливает начало фрагмента который diff считает, как добавленное перед строками из файл2. Отсюда берётся число строк этого фрагмента. Заголовок этого фрагмента должен выглядеть как @@ -0,0 +1,n @@, где n - количество его строк. n равное 1 опускается.
Если в файл1 есть все строки из файл2, но между ними есть ещё другие, в этом случае diff выдаст больше двух срок начинающихся с @@ и ни одного минуса, последняя группа команд отслеживает это.

Комментариев нет:

Отправить комментарий