Поиск и замена нескольких строк в огромном текстовом файле

#regex #logging #text #replace #find

#регулярное выражение #ведение журнала #текст #заменить #Найти

Вопрос:

У меня есть огромный файл журнала (почти 6 ГБ) игрового сервера, заполненный миллионами ошибок (в то время каждую секунду возникали сотни), помимо полезных записей, которые необходимо сохранить. Я хотел бы удалить все строки, включая ошибку, сохранив те, в которых отображаются сообщения чата или другая информация.

Однако я не могу просто удалить строки, которые я хотел бы удалить, потому что сообщения об ошибках не всегда одинаковы и всегда требуют разного количества строк. Короче говоря, я просто не могу определить, какие строки содержат ошибку. Для этого мне нужно регулярное выражение. Я искал программу, которая соответствует моим целям. Однако я еще не нашел ни одной. sed (редактор потоков) мог бы выполнить такую работу, например, поскольку ему не потребовалось бы слишком много ресурсов для обработки такого огромного файла. Однако он не поддерживает поиск и замену по нескольким строкам.

Таким образом, существует ли программа, которая поддерживает поиск и замену регулярных выражений в огромных текстовых файлах в нескольких строках? Или рекомендуется написать свой собственный скрипт для выполнения этой работы?

Файл журнала выглядит следующим образом:

 2011-03-02 01:43:00 [INFO] <admin> CraftBook is causing errors. 
2011-03-02 01:43:01 [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
java.lang.NoSuchMethodError: com.sk89q.worldedit.blocks.BlockType.isRedstoneBlock(I)Z
    at com.sk89q.craftbook.bukkit.MechanicListenerAdapter$MechanicBlockListener.onBlockRedstoneChange(MechanicListenerAdapter.java:174)
    at net.minecraft.server.BlockButton.a(BlockButton.java:170)
    at net.minecraft.server.ItemInWorldManager.a(ItemInWorldManager.java:160)
    at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:482)
    at net.minecraft.server.Packet15Place.a(SourceFile:57)
    at net.minecraft.server.NetworkManager.a(SourceFile:230)
    at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:75)
    at net.minecraft.server.NetworkListenThread.a(SourceFile:100)
    at net.minecraft.server.MinecraftServer.h(MinecraftServer.java:357)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:272)
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:366)
2011-03-02 01:43:01 [INFO] <admin> Is it working yet? 
2011-03-02 01:43:01 [INFO] <admin> Not really. 
2011-03-02 01:43:01 [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
java.lang.NoSuchMethodError: com.sk89q.worldedit.blocks.BlockType.isRedstoneBlock(I)Z
    at com.sk89q.craftbook.bukkit.MechanicListenerAdapter$MechanicBlockListener.onBlockRedstoneChange(MechanicListenerAdapter.java:174)
    at net.minecraft.server.MinecraftServer.h(MinecraftServer.java:348)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:272)
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:366)
2011-03-02 01:43:02 [INFO] <admin> I hope we find a solution as soon as ever possible. 
  

Желаемый результат был бы следующим:

 2011-03-02 01:43:00 [INFO] <admin> CraftBook is causing errors.
2011-03-02 01:43:01 [INFO] <admin> Is it working yet? 
2011-03-02 01:43:01 [INFO] <admin> Not really. 
2011-03-02 01:43:02 [INFO] <admin> I hope we find a solution as soon as ever possible.
  

Как вы можете видеть, файл журнала снова и снова содержит одну и ту же ошибку. Несмотря на то, что оно всегда начинается с даты и времени, за которыми следует [СЕРЬЕЗНЫЙ] Не удалось передать событие REDSTONE_CHANGE в CraftBookMechanisms и заканчивается на на net.minecraft.server.ThreadServerApplication.run(исходный файл: 366), промежуточное сообщение об ошибке каждый раз отличается. Вот почему я не могу просто заменить сообщение об ошибке пустой строкой.

Существует ли регулярное выражение, которое могло бы помочь мне избавиться от всех строк, содержащих ошибку, но сохранить оставшиеся строки? Таким образом, мой файл журнала уменьшился бы до размера менее 50 МБ, как это было до того, как все эти ошибки были вызваны моим сервером из-за неисправного плагина.

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

1. Быстрый вопрос (и, возможно, упрощенный подход), было бы нормально, если бы вы просто сохранили строки, содержащие строку <admin> ?

2. Нет, приведенный выше пример на самом деле не показывает вам все возможные выходные данные сервера. На самом деле, сервер может также записывать другие данные в файл журнала. Сервер также записывает другие ошибки в нескольких строках в server.log. Я бы хотел сохранить эти ошибки. Я просто хочу иметь возможность избавиться от конкретной ошибки, упомянутой в примере.

Ответ №1:

Этот скрипт на Python выполняет один проход через файл журнала, считанный из stdin, выводя отфильтрованные сообщения журнала в стандартный вывод.

Оно использует регулярное выражение для сопоставления строк, которые отмечают начало сообщения журнала (например, строки, начинающейся с 2011-03-02 01:43:00 [ ).

Если строка, с которой начинается сообщение журнала, содержит [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms , скрипт отбрасывает все строки между этой строкой и строкой, содержащей начало следующего сообщения журнала. В противном случае выводится строка. Вы можете представить это как конечный автомат с двумя состояниями, которые соответствуют тому, пропускает скрипт строки или выводит строки.

 import sys
import re

START_OF_MESSAGE_RE = r"^d{4}-d{2}-d{2} d{2}:d{2}:d{2}"
ERROR_RE = START_OF_MESSAGE_RE   r' [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms$'
skip_until_next_message = False

for line in sys.stdin:
    line = line.rstrip()
    if re.match(START_OF_MESSAGE_RE, line):
        if re.match(ERROR_RE, line):
            skip_until_next_message = True
        else:
            skip_until_next_message = False
    if not skip_until_next_message:
        print line
  

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

 2011-03-02 01:43:00 [INFO] <admin> CraftBook is causing errors. 
2011-03-02 01:43:01 [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
java.lang.NoSuchMethodError: com.sk89q.worldedit.blocks.BlockType.isRedstoneBlock(I)Z
    at com.sk89q.craftbook.bukkit.MechanicListenerAdapter$MechanicBlockListener.onBlockRedstoneChange(MechanicListenerAdapter.java:174)
    at net.minecraft.server.BlockButton.a(BlockButton.java:170)
    at net.minecraft.server.ItemInWorldManager.a(ItemInWorldManager.java:160)
    at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:482)
    at net.minecraft.server.Packet15Place.a(SourceFile:57)
    at net.minecraft.server.NetworkManager.a(SourceFile:230)
    at net.minecraft.server.NetServerHandler.a(NetServerHandler.java:75)
    at net.minecraft.server.NetworkListenThread.a(SourceFile:100)
    [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
    at net.minecraft.server.MinecraftServer.h(MinecraftServer.java:357)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:272)
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:366)
2011-03-02 01:43:01 [INFO] <admin> Is it working yet? 
2011-03-02 01:43:01 [INFO] <admin> Not really. 
2011-03-02 01:43:01 [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
java.lang.NoSuchMethodError: com.sk89q.worldedit.blocks.BlockType.isRedstoneBlock(I)Z
    at com.sk89q.craftbook.bukkit.MechanicListenerAdapter$MechanicBlockListener.onBlockRedstoneChange(MechanicListenerAdapter.java:174)
    at net.minecraft.server.MinecraftServer.h(MinecraftServer.java:348)
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:272)
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:366)
2011-03-02 01:43:02 [INFO] <admin> I hope we find a solution as soon as ever possible. 
2011-03-02 01:43:01 [SEVERE] Another multi
line
log
message
2011-03-02 01:43:01 [INFO] <admin> Here's the error: [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
  

И вот результат:

 $ python minecraftlog.py < minecraft.log 
2011-03-02 01:43:00 [INFO] <admin> CraftBook is causing errors.
2011-03-02 01:43:01 [INFO] <admin> Is it working yet?
2011-03-02 01:43:01 [INFO] <admin> Not really.
2011-03-02 01:43:02 [INFO] <admin> I hope we find a solution as soon as ever possible.
2011-03-02 01:43:01 [SEVERE] Another multi
line
log
message
2011-03-02 01:43:01 [INFO] <admin> Here's the error: [SEVERE] Could not pass event REDSTONE_CHANGE to CraftBookMechanisms
  

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

1. Спасибо вам за написание этого скрипта на Python. Это было удивительно просто! Фактически, он смог обработать весь файл (6 ГБ) всего за минуту. После этого мне просто нужно было добавить возвраты каретки, которые были удалены скриптом Python, запущенным на компьютере с Linux, с помощью простой tr-команды, которую я нашел в Интернете. Теперь я, наконец, получил чистый файл журнала размером 50 МБ вместо 6 ГБ, который был раньше, включая все ошибки.

Ответ №2:

Кажется, лучший подход заключается в сопоставлении строк, которые вы хотите сохранить, косвенно «удаляя» строки, которые вам не нужны:

Следующего скрипта на Perl должно быть достаточно:

 while (<>) {
  next unless /^d{4}-d{2}-d{2}sd{2}:d{2}:d{2}s[INFO]/;
  print;
}
  

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

1. Это тоже был бы подход. Однако сервер также выводит другие данные, не показанные в примере выше. Это также может привести к записи других ошибок в server.log. Я бы хотел сохранить их и избавиться только от конкретной ошибки, упомянутой в примере. Кроме того, не каждая строка начинается с даты и времени. В приведенной информации есть разрывы строк. Вот почему вы не можете просто сопоставить строки, которые я хотел бы сохранить.

2. Затем посмотрите ответ Джоша Розена, но я бы сделал что-то подобное в Perl. По сути, вы повторяете ввод в журнал, но устанавливаете флаг подавления, если совпадаете с первой строкой сообщения об ошибке, которое хотите исключить. Повторное включение, повторяющееся при переходе к следующему сообщению журнала «не исключать».