У меня есть zip-файл размером 1,5 ГБ.
Его содержимое представляет собой один смешной большой текстовый файл (60 ГБ), и в настоящее время у меня недостаточно свободного места на диске, чтобы извлечь все, и при этом я не хочу извлекать все это, даже если бы у меня было.
Что касается моего варианта использования, было бы достаточно, если бы я мог проверять части содержимого.
Следовательно, я хочу разархивировать файл как поток и получить доступ к диапазону файла (как можно через голову и хвост в обычном текстовом файле).
Либо по памяти (например, извлечение максимум 100 КБ, начиная с отметки 32 ГБ), либо по строкам (дайте мне строки обычного текста 3700-3900).
Есть ли способ достичь этого?
text-processing
zip
k0pernikus
источник
источник
Ответы:
Обратите внимание, что
gzip
может извлекатьzip
файлы (по крайней мере, первая запись вzip
файле). Так что если в этом архиве только один огромный файл, вы можете сделать:Например, извлечь 20 строк, начиная с 3000-й.
Или:
Для того же самого с байтами (предполагая
head
реализацию, которая поддерживает-c
).Для любого произвольного члена в архиве Unixy-способом:
С помощью
head
встроенной функцииksh93
(например, когда/opt/ast/bin
впереди$PATH
) вы также можете сделать:Обратите внимание, что в любом случае
gzip
/bsdtar
/unzip
всегда нужно будет распаковывать (и отбрасывать здесь) весь раздел файла, который ведет к той части, которую вы хотите извлечь. Это зависит от того, как работает алгоритм сжатия.источник
gzip
справится, будут ли работать другие утилитыzcat
,zless
работающие с z, ( и т. Д.)?gzip
(как правило, это правдаzless
, не обязательно изzcat
которых в некоторых системах все еще только для чтения.Z
файлов), да.Одно решение, использующее unzip -p и dd, например, для извлечения 10 КБ со смещением 1000 блоков:
Примечание: я не пробовал это с действительно большими данными ...
источник
unzip -l ARCHIVE
для просмотра содержимого архива иunzip -p ARCHIVE PATH
извлечения содержимого одного объектаPATH
в стандартный вывод.dd
на трубах с графом или пропустить ненадежно , как он будет делать , что многиеread()
х до до 1024 байт. Таким образом, он гарантированно будет работать правильно только приunzip
записи в канал кусками, размер которых кратен 1024.Если у вас есть контроль над созданием этого большого zip-файла, почему бы не рассмотреть возможность использования комбинации
gzip
иzless
?Это позволит вам использовать его
zless
как пейджер и просматривать содержимое файла без необходимости извлечения.Если вы не можете изменить формат сжатия, то это, очевидно, не сработает. Если это так, я чувствую, что
zless
это довольно удобно.источник
Чтобы просмотреть конкретные строки файла, передайте вывод в потоковый редактор Unix sed . Это может обрабатывать произвольно большие потоки данных, так что вы даже можете использовать их для изменения данных. Чтобы просмотреть строки 3700-3900, как вы просили, выполните следующее.
источник
sed -n 3700,3900p
продолжит чтение до конца файла. Лучше использовать,sed '3700,$!d;3900q'
чтобы избежать этого, или даже вообще более эффективно:tail -n +3700 | head -n 201
Я задавался вопросом, можно ли сделать что-нибудь более эффективное, чем распаковка от начала файла до момента. Похоже, что ответ - нет. Однако на некоторых процессорах (Skylake) процессор
zcat | tail
не разгоняется до полной тактовой частоты. См. ниже. Пользовательский декодер может избежать этой проблемы и сохранить системные вызовы записи канала, и может быть на ~ 10% быстрее. (Или ~ 60% быстрее на Skylake, если вы не настраиваете параметры управления питанием).Лучшее, что вы могли бы сделать с настраиваемым zlib с
skipbytes
функцией - это проанализировать символы в блоке сжатия, чтобы добраться до конца, не выполняя работу по фактической реконструкции распакованного блока. Это может быть значительно быстрее (возможно, по крайней мере, в 2 раза), чем вызов обычной функции декодирования zlib для перезаписи того же буфера и перемещения вперед в файле. Но я не знаю, написал ли кто-нибудь такую функцию. (И я думаю, что это на самом деле не работает, если файл не был написан специально, чтобы позволить декодеру перезапуститься с определенного блока)Я надеялся, что есть способ пропустить блоки Deflate без их декодирования, потому что это будет намного быстрее. Дерево Хаффмана отправляется в начале каждого блока, так что вы можете декодировать с начала любого блока (я думаю). О, я думаю, что состояние декодера больше, чем дерево Хаффмана, это также предыдущие 32 КБ декодированных данных, и это не сбрасывается / не забывается через границы блоков по умолчанию. На одни и те же байты можно постоянно ссылаться, поэтому они могут появляться буквально один раз в гигантском сжатом файле. (например, в файле журнала имя хоста, вероятно, остается «горячим» в словаре сжатия все время, и каждый его экземпляр ссылается на предыдущий, а не на первый).
В
zlib
руководстве говорится, что вы должны использоватьZ_FULL_FLUSH
при вызове,deflate
если вы хотите, чтобы сжатый поток был доступен для поиска до этого момента. Он «сбрасывает состояние сжатия», поэтому я думаю, что без этого обратные ссылки могут перейти в предыдущий блок (ы). Таким образом, если ваш zip-файл не был написан со случайными блоками полного сброса (например, каждый 1G или что-то еще не оказало бы незначительного влияния на сжатие), я думаю, вам придется выполнять большую часть работы по декодированию до того уровня, который вы хотите, чем я был изначально мышление. Я думаю, что вы, вероятно, не можете начать в начале любого блока.Остальное было написано, пока я думал, что можно будет просто найти начало блока, содержащего первый нужный байт, и декодировать оттуда.
Но, к сожалению, начало блока Deflate не указывает, как долго это будет , для сжатых блоков. Несжимаемые данные могут быть закодированы с несжатым типом блока, который имеет 16-битный размер в байтах на передней панели, но сжатые блоки этого не делают: RFC 1951 описывает формат довольно читабельно . Блоки с динамическим кодированием Хаффмана имеют дерево в передней части блока (поэтому декомпрессор не должен искать в потоке), поэтому перед записью компрессор должен сохранить весь (сжатый) блок в памяти.
Максимальное расстояние обратного отсчета составляет всего 32 кБ, поэтому компрессору не нужно хранить много несжатых данных в памяти, но это не ограничивает размер блока. Блоки могут быть длиной в несколько мегабайт. (Это достаточно для того, чтобы поиск диска стоил того даже на магнитном диске, по сравнению с последовательным считыванием в память и просто пропуском данных в ОЗУ, если можно было найти конец текущего блока, не анализируя его).
zlib создает блоки как можно дольше: согласно Марку Адлеру , zlib начинает новый блок только тогда, когда заполнен буфер символов, который по умолчанию равен 16 383 символам (литералам или совпадениям)
Я распаковал вывод
seq
(который чрезвычайно избыточен и, следовательно, вероятно, не очень хороший тест), ноpv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -c
на этом он работает со скоростью ~ 62 МБ / с сжатых данных на Skylake i7-6700k с частотой 3,9 ГГц и оперативной памятью DDR4-2666. Это 246 МБ / с декомпрессированных данных, что является частичным изменением по сравнению соmemcpy
скоростью ~ 12 ГБ / с для блоков слишком большого размера, чтобы поместиться в кэш.(С
energy_performance_preference
набором по умолчаниюbalance_power
вместоbalance_performance
губернатор внутреннего процессора Skylake решает только работать на 2.7GHz, ~ 43 MiB / с сжатыми данными. Я использую ,sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'
чтобы настроить его. Возможно , такие частые системные вызовы не выглядят как настоящие CPU переплет работать с блоком управления питанием.)TL: DR:
zcat | tail -c
привязан к процессору даже на быстром процессоре, если только у вас не очень медленные диски. gzip использовал 100% процессора, на котором он работал (и выполнял 1,81 инструкции в такт, согласноperf
), иtail
использовал 0,162 процессора, на котором он работал (0,58 IPC). В остальном система в основном простаивала.Я использую Linux 4.14.11-1-ARCH, в которой по умолчанию включен KPTI для обхода Meltdown, поэтому все эти
write
системные вызовыgzip
обходятся дороже, чем раньше: /Наличие встроенного поиска для
unzip
илиzcat
(но все еще использующего обычнуюzlib
функцию декодирования) сохранит все эти записи канала и заставит процессоры Skylake работать на полной тактовой частоте. (Этот разгон для некоторых видов нагрузки является уникальным для Intel Skylake и более поздних версий, которые переносят процесс принятия решений о частоте ЦП из ОС, потому что у них больше данных о том, что делает ЦП, и они могут увеличивать / уменьшать скорость быстрее. как правило, хорошо, но здесь приводит к тому, что Skylake не разгоняется до полной скорости при более консервативной настройке регулятора).Никакие системные вызовы, просто переписывание буфера, который помещается в кэш L2 до тех пор, пока вы не достигнете желаемой позиции начального байта, вероятно, будет иметь разницу как минимум на несколько%. Может быть, даже 10%, но я просто придумываю цифры здесь. Я не стал
zlib
подробно рассказывать, насколько велика его площадь кэш-памяти и сколько сбрасывает TLB (и, следовательно, очищает uop-cache) при каждом системном вызове при включенном KPTI.Есть несколько программных проектов, которые добавляют индекс поиска в формат файла gzip . Это не поможет вам, если вы не сможете заставить кого-либо создавать для вас сжатые файлы, которые могут быть найдены для поиска, но другие будущие читатели могут выиграть.
Предположительно ни один из этих проектов не имеет функции декодирования , которая знает , как пропустить через поток Deflate без индекса, потому что они предназначены только для работы , когда индекс является доступен.
источник
Вы можете открыть zip-файл в сеансе Python, используя
zf = zipfile.ZipFile(filename, 'r', allowZip64=True)
и, открыв его, вы можете открыть для чтения любой файл в zip-архиве, прочитать строки и т. Д. Из него, как если бы это был обычный файл.источник