Недавно я задал вопрос о том, как удалить символ новой строки, если это происходит после другого конкретного символа.
Инструменты обработки текста Unix очень мощные, но почти все они имеют дело со строками текста, что прекрасно в большинстве случаев, когда ввод вписывается в доступную память.
Но что мне делать, если я хочу заменить текстовую последовательность в огромном файле, который не содержит новых строк?
Например, заменить <foobar>
на \n<foobar>
без чтения ввод построчно? (так как есть только одна строка и длина 2.5G символов).
text-processing
MattBianco
источник
источник
perl
илиpython
?gsar
( home.online.no/~tjaberg ), который я попробую.Ответы:
Первое, что приходит мне в голову, когда я сталкиваюсь с проблемой такого типа, это изменение разделителя записей. В большинстве инструментов это установлено
\n
по умолчанию, но это можно изменить. Например:Perl
объяснение
-0
: это устанавливает разделитель входной записи на символ, учитывая его шестнадцатеричное значение . В этом случае я устанавливаю>
чье шестнадцатеричное значение3E
. Общий формат есть-0xHEX_VALUE
. Это просто хитрость, чтобы разбить линию на управляемые куски.-pe
: печатать каждую строку ввода после применения скрипта, заданного-e
.s/<foobar>/\n$&/
простая замена. Это$&
то, что было подобрано, в этом случае<foobar>
.AWK
объяснение
RS="<"
: установите разделитель входной записи на>
.gsub(/foobar>/,"\n<foobar>")
: заменить все случаиfoobar>
с\n<foobar>
. Обратите внимание, что посколькуRS
было установлено значение<
, все<
удаляются из входного файла (вот как этоawk
работает), поэтому нам нужно сопоставитьfoobar>
(без<
) и заменить на\n<foobar>
.printf "%s",$0
: вывести текущую «строку» после замены.$0
текущая запись,awk
поэтому она будет содержать все, что было до<
.Я протестировал их на однострочном файле объемом 2,3 ГБ, созданном с помощью следующих команд:
И то,
awk
и другоеperl
использовали незначительное количество памяти.источник
Tie::File
perldoc.perl.org/Tie/File.html . Я думаю, что это лучшая особенностьPerl
при работе с огромными файлами.Tie::File
это основной модуль с тех порv5.7.3
.gsar (общий поиск и замена) - очень полезный инструмент именно для этой цели.
В большинстве ответов на этот вопрос используются основанные на записях инструменты и различные приемы, позволяющие адаптировать их к проблеме, например переключение символа разделителя записей по умолчанию на то, что, как предполагается, происходит во входных данных достаточно часто, чтобы не сделать каждую запись слишком большой для обработки.
Во многих случаях это очень хорошо и даже читабельно. Я как проблемы , которые могут быть легко / эффективно решено с помощью всюду доступными инструментами , такими как
awk
,tr
,sed
и оболочка Борна.Выполнение бинарного поиска и замены в произвольном огромном файле со случайным содержимым не очень подходит для этих стандартных инструментов Unix.
Некоторые из вас могут подумать, что это обман, но я не понимаю, как использование правильного инструмента для работы может быть неправильным. В этом случае программа C называется ,
gsar
что под лицензией GPL v2 , так что меня удивляет совсем немного , что нет никакого пакета для этого очень полезного инструмента ни папуасского , RedHat , ни убунту .gsar
использует двоичный вариант алгоритма поиска строки Бойера-Мура .Использование просто:
где
-F
означает режим «фильтра», то есть чтение,stdin
запись вstdout
. Есть методы для работы с файлами.-s
указывает строку поиска и-r
замену. Обозначение двоеточия может использоваться для указания произвольных значений байтов.Режим без учета регистра поддерживается (
-i
), но регулярных выражений не поддерживается, поскольку алгоритм использует длину строки поиска для оптимизации поиска.Инструмент также может быть использован только для поиска, немного похоже
grep
.gsar -b
выводит смещения байтов совпадающей строки поиска иgsar -l
печатает имя файла и количество совпадений, если таковые имеются, как в сочетанииgrep -l
сwc
.Инструмент был написан Тормодом Тьябергом (начальный) и Хансом Питером Верном (улучшения).
источник
gsar
.В узком случае, когда целевые и замещающие строки имеют одинаковую длину, может помочь отображение памяти . Это особенно полезно, если замену необходимо выполнить на месте. Вы в основном отображаете файл в виртуальную память процесса, а адресное пространство для 64-битной адресации огромно. Обратите внимание, что файл не обязательно отображается одновременно в физической памяти , поэтому можно обрабатывать файлы, размер которых в несколько раз превышает размер физической памяти, доступной на компьютере.
Вот пример Python, который заменяет
foobar
наXXXXXX
источник
Для этого есть много инструментов:
dd
это то, что вы хотите использовать, если вы хотите заблокировать файл - надежно читать только определенное количество байтов только определенное количество раз. Портативно обрабатывает блокировку и разблокировку файловых потоков:tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null
UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N
Я также использую
tr
выше, потому что он может обрабатывать преобразование любого байта ASCII в любой другой (или, в этом случае, удаление любого байта ASCII, который не является печатным символом без пробела). Это то, что я использовал в ответ на ваш другой вопрос сегодня утром, на самом деле, когда я сделал:Есть много похожих . Этот список должен содержать наименьшее подмножество общего знаменателя, с которым вы можете познакомиться.
Но если бы я собирался выполнить обработку текста на 2,5 ГБ двоичного файла, я мог бы начать с
od
. Он может дать вам одинoctal dump
или несколько других форматов. Вы можете указать все виды опций - но я просто сделаю один байт на строку в\C
экранированном формате:Данные, которые вы получите,
od
будут регулярными с любым указанным вами интервалом - как я покажу ниже. Но сначала - вот ответ на ваш вопрос:Это немного выше разграничивает на
\n
ewlines,\0
нули,\t
абс и<spaces>
при сохранении\C
сбежавшего строки для разделителя. Обратите внимание на используемые функцииH
иx
- каждый раз, когдаsed
встречается разделитель, он заменяет содержимое своих буферов памяти. Таким образом,sed
хранится только столько информации, сколько необходимо для надежного разграничения файла, и он не поддается переполнению буфера - нет, то есть до тех пор, пока он действительно сталкивается со своими разделителями. До тех пор, пока это происходит,sed
будет продолжать обрабатывать свои входные данные иod
будет предоставлять их до тех пор, пока они не встретятсяEOF
.Как есть, его вывод выглядит так:
Так что, если я хочу
foobar
:Теперь, если вы хотите использовать
C
escape-коды, это довольно просто - потомуsed
что двойная\\
обратная косая черта уже экранирована от всех одиночных обратных косых черт ввода, так чтоprintf
исключение из неxargs
будет иметь проблем с выводом в вашу спецификацию. Но естьxargs
кавычки оболочки, поэтому вам нужно будет снова заключить в кавычки:Это можно было бы так же легко сохранить в переменной оболочки и вывести позже таким же образом. Последний
sed
вставляет\
обратную косую черту перед каждым символом на входе, и все.И вот как все это выглядит прежде, чем когда-либо
sed
овладевает этим:источник
Awk работает с последовательными записями. Он может использовать любой символ в качестве разделителя записей (кроме нулевого байта во многих реализациях). Некоторые реализации поддерживают произвольные регулярные выражения (не совпадающие с пустой строкой) в качестве разделителя записей, но это может быть громоздким, поскольку разделитель записей усекается с конца каждой записи до того, как он будет помещен в
$0
(GNU awk устанавливает переменнуюRT
в разделитель записей) это было снято с конца текущей записи). Обратите внимание, чтоprint
вывод завершается разделителем выходных записей,ORS
который по умолчанию является новой строкой и устанавливается независимо от разделителя входных записейRS
.Вы можете эффективно выбрать другой символ как разделитель записей для других инструментов (
sort
,sed
, ...) путем замены новой строки с этим символом сtr
.Многие текстовые утилиты GNU поддерживают использование нулевого байта вместо новой строки в качестве разделителя.
источник