как эффективно подставить последнее вхождение шаблона в большой файл - PullRequest
1 голос
/ 21 декабря 2011

Имеется файл со следующим содержимым:

<root>
<a></a>
<b></b>
</root>

Команда должна вывести:

<root>
<a></a>
<b></b>

Вещи, которые я пытался использовать GNU Win32 порт sed:

Удалите последние две строки.

Это быстро, но предполагает, что </root> - вторая до последней строки и приведет к ошибке, если это не так.

sed -e '$d' test.xml | sed -e '$d'

Замена всех вхождений </root> пустой строкой.

Это работает, но медленнее, чем первое решение, и будет прерываться, если есть вложенные<root> элементов (маловероятно).

sed -e 's|</root>||' test.xml

Файл, с которым я имею дело, может быть большим, поэтому эффективность важна.

Есть ли способ ограничить подстановку sed последним вхождением в файле?Или есть какая-нибудь другая утилита, которая будет быстрее?

Ответы [ 5 ]

2 голосов
/ 21 декабря 2011

Использование Perl с File :: Backwards должно быть очень быстрым (я знаю, относительно, но все же ...).В Perlfaq5 есть тема о том, как пройти файл назад и удалить строки.Вы можете проверить свой шаблон, используя код этой темы в качестве отправной точки.

1 голос
/ 22 декабря 2011

Это может работать для вас:

 sed '/<\/root>/,/<root>/{/<\/root>/{h;d};H;//{x;p};${x;s/[^\n]*\n//p};d}' file

Это предполагает, что каждый тег <root> соответствует закрывающему тегу </root> и эти теги встречаются в отдельных строках (в соответствии с примером).

Объяснение:

  1. Сосредоточиться на строках между закрывающим тегом </root> и открывающим тегом <root> или концом файла.
  2. Если это такзакрывающий тег </root>, сохраните его в области удержания (HS), затем удалите его и начните новый цикл.
  3. Для всех других линий в фокусе (см. пункт 1) добавьте их в HS.
  4. Если это и открывающий тег <root>, переключитесь на HS и распечатайте его содержимое.
  5. Если это конец файла, то есть между тегом </root> и последней строкойфайла, своп к HS, удалите первую строку, т. е. закрывающий тег </root> и напечатайте остаток.
  6. Для всех строк в фокусе удалите и начните новый цикл.

Альтернативное решение с двумя проходами:

sed -n '/<\/root>/=' file | sed -n '$s/$/d/p' | sed -f - file

Объяснение:

  1. Распечатайте номера строк closing </root> tags
  2. Генерация команды sed для удаления из номера последней соответствующей строки.
  3. Передача команды в экземпляр sed, читающий исходный файл.
1 голос
/ 21 декабря 2011

Как насчет использования awk для этого.

AWK:

awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' filename

Первый /pattern/{action} ищет строки с только </root>.Это образец находит это, действие игнорирует это.

Второй /pattern/{action} оператор ищет строки, содержащие </root> в любом месте в строке.Если шаблон находит его, sub function заменяет его ничем и печатает остаток строки.

Третье действие, равное 1, верно для всех строкв них нет шаблона </root>.Если это находит, это печатает это.

Я сделал быстрый тест, и это был результат -

Тест:

[jaypal:~/Temp] cat tmp
<root>
<a></a>
<b></b>
</root>
<root>
<a></a>
<b></b>
</root><root>
<a></a>
<b></b></root>
[jaypal:~/Temp] awk '/^<\/root>$/{next}/<\/root>/{sub(/<\/root>/,"");print;next}1' tmp
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>
<root>
<a></a>
<b></b>

SED:

Это должнотакже работа.Хотя это удалит все </root>, а не только последнее вхождение.

sed '/<\/root>/,$s///' filename
1 голос
/ 21 декабря 2011

С sed:

sed -e ':a;N;$!ba;s|\(.*\)</root>\n\(.*\)|\1\2|'
0 голосов
/ 22 декабря 2011

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

$time command

На мой взгляд, нет ничего быстрее, чем grep.попробуйте его с помощью awk index (), чтобы узнать, быстрее ли это.

...