วิธีการคลายบีบอัดไฟล์ข้อความธรรมดาขนาดใหญ่บีบอัดบางส่วน?


19

ฉันมีไฟล์ zip ขนาด 1.5 GB

เนื้อหาของมันคือไฟล์ข้อความตัวอักษรขนาดใหญ่ที่น่าขัน (60 GB) และตอนนี้ฉันมีพื้นที่เหลือบนดิสก์ไม่เพียงพอที่จะดึงมันออกมาทั้งหมดหรือฉันต้องการที่จะดึงมันออกมาทั้งหมดแม้ว่าฉันจะมีก็ตาม

สำหรับกรณีการใช้งานของฉันมันจะพอเพียงถ้าฉันสามารถตรวจสอบบางส่วนของเนื้อหา

ดังนั้นฉันต้องการแตกไฟล์เป็นสตรีมและเข้าถึงช่วงของไฟล์ (เช่นเดียวกับที่สามารถทำได้ผ่านส่วนหัวและส่วนท้ายในไฟล์ข้อความปกติ)

ไม่ว่าจะโดยหน่วยความจำ (เช่นแยกสูงสุด 100kb เริ่มต้นที่เครื่องหมาย 32GB) หรือตามบรรทัด (ให้บรรทัดข้อความธรรมดา 3700-3900)

มีวิธีการที่จะบรรลุเป้าหมายนั้นหรือไม่?


1
น่าเสียดายที่ไม่สามารถค้นหาไฟล์แต่ละไฟล์ได้ภายใน zip ดังนั้นการแก้ปัญหาใด ๆ จะเกี่ยวข้องกับการอ่านไฟล์จนถึงจุดที่คุณสนใจ
ปลั๊กอิน

5
@plugwash เมื่อฉันเข้าใจคำถามเป้าหมายไม่ใช่เพื่อหลีกเลี่ยงการอ่านไฟล์ zip (หรือแม้แต่ไฟล์ที่คลายการบีบอัด) แต่เพื่อหลีกเลี่ยงการจัดเก็บไฟล์ที่คลายการบีบอัดทั้งหมดไว้ในหน่วยความจำหรือบนดิสก์ โดยทั่วไปการรักษาไฟล์ขยายมาเป็นกระแส
ShreevatsaR

คำตอบ:


28

โปรดทราบว่าgzipสามารถแยกzipไฟล์ (อย่างน้อยรายการแรกในzipไฟล์) ดังนั้นหากมีไฟล์ขนาดใหญ่เพียงไฟล์เดียวในที่เก็บถาวรนั้นคุณสามารถทำได้:

gunzip < file.zip | tail -n +3000 | head -n 20

หากต้องการแยก 20 บรรทัดที่เริ่มต้นด้วยบรรทัดที่ 3000 เช่น

หรือ:

gunzip < file.zip | tail -c +3000 | head -c 20

สำหรับสิ่งเดียวกันกับไบต์ (สมมติว่ามีheadการใช้งานที่รองรับ-c)

สำหรับสมาชิกใด ๆ ในการจัดเก็บถาวรในลักษณะ Unixy:

bsdtar xOf file.zip file-to-extract | tail... | head...

ด้วยheadbuiltin of ksh93(เหมือนตอนที่/opt/ast/binอยู่ข้างหน้า$PATH) คุณสามารถทำได้:

.... | head     -s 2999      -c 20
.... | head --skip=2999 --bytes=20

โปรดทราบว่าในกรณีใด ๆgzip/ bsdtar/ unzipจะต้องคลายการบีบอัด (และยกเลิกที่นี่) ส่วนทั้งหมดของไฟล์ที่นำไปสู่ส่วนที่คุณต้องการแยก นั่นเป็นวิธีการทำงานของอัลกอริธึมการบีบอัด


หากgzipสามารถจัดการกับมันจะอีก "Z ตระหนักถึง" สาธารณูปโภค ( zcat, zlessฯลฯ ) นอกจากนี้ยังทำงานหรือไม่
ivanivan

@ivanivan บนระบบที่ใช้พื้นฐานgzip(โดยทั่วไปเป็นจริงzlessไม่จำเป็นต้องใช้zcatกับบางระบบที่ยังคงอ่าน.Zไฟล์เท่านั้น) ใช่
Stéphane Chazelas

14

ทางออกหนึ่งที่ใช้ unzip -p และ dd ตัวอย่างเช่นเพื่อแยก 10kb ด้วย 1,000 blocs offset:

$ unzip -p my.zip | dd ibs=1024 count=10 skip=1000 > /tmp/out

หมายเหตุ: ฉันไม่ได้ลองกับข้อมูลขนาดใหญ่จริงๆ ...


ในกรณีทั่วไปของไฟล์มากกว่าหนึ่งครั้งในไฟล์เก็บถาวรหนึ่งไฟล์สามารถใช้unzip -l ARCHIVEเพื่อแสดงรายการเนื้อหาที่เก็บถาวรและunzip -p ARCHIVE PATHเพื่อแยกเนื้อหาของวัตถุเดียวPATHไปยัง stdout
David Foerster

3
โดยทั่วไปใช้ddในท่อที่มีการนับหรือข้ามจะไม่น่าเชื่อถือเท่าที่มันจะทำว่าหลายread()ของถึง 1024 ไบต์ ดังนั้นจึงรับประกันว่าจะทำงานได้อย่างถูกต้องหากunzipเขียนไปยังท่อเป็นชิ้น ๆ ที่มีขนาดเท่ากับ 1024
Stéphane Chazelas

4

หากคุณมีอำนาจควบคุมการสร้างไฟล์ zip ขนาดใหญ่นั้นทำไมไม่ลองใช้การรวมกันของgzipและzless?

นี่จะอนุญาตให้คุณใช้zlessเป็นเพจเจอร์และดูเนื้อหาของไฟล์โดยไม่ต้องกังวลกับการแตกไฟล์

หากคุณไม่สามารถเปลี่ยนรูปแบบการบีบอัดได้จะทำให้ไม่สามารถใช้งานได้ ถ้าเป็นเช่นนั้นฉันรู้สึกว่าzlessค่อนข้างสะดวก


1
ฉันไม่. ฉันกำลังดาวน์โหลดไฟล์ซิปที่มาจาก บริษัท ภายนอก
k0pernikus

3

เพื่อดูสายที่เฉพาะเจาะจงของไฟล์ท่อส่งออกไปยังบรรณาธิการกระแส Unix, sed สิ่งนี้สามารถประมวลผลสตรีมข้อมูลขนาดใหญ่โดยพลการดังนั้นคุณสามารถใช้มันเพื่อเปลี่ยนข้อมูลได้ ในการดูบรรทัด 3700-3900 ตามที่คุณถามให้เรียกใช้รายการต่อไปนี้

unzip -p file.zip | sed -n 3700,3900p

7
sed -n 3700,3900pจะอ่านต่อไปจนกว่าจะสิ้นสุดไฟล์ มันเป็นเรื่องดีที่จะใช้sed '3700,$!d;3900q'เพื่อหลีกเลี่ยงที่หรือแม้กระทั่งโดยทั่วไปมีประสิทธิภาพมากขึ้น:tail -n +3700 | head -n 201
Stéphane Chazelas

3

ฉันสงสัยว่ามันเป็นไปได้หรือไม่ที่จะทำสิ่งใดให้มีประสิทธิภาพมากกว่าการคลายการบีบอัดจากจุดเริ่มต้นของไฟล์จนถึงจุด ปรากฏว่าคำตอบคือไม่ อย่างไรก็ตามในบางซีพียู (Skylake) zcat | tailไม่ได้เพิ่มความเร็วของนาฬิกาจนเต็ม ดูด้านล่าง ตัวถอดรหัสที่กำหนดเองสามารถหลีกเลี่ยงปัญหานั้นและบันทึกการเรียกใช้การเขียนไปป์ของระบบและอาจเร็วกว่า ~ 10% (หรือเร็วกว่า ~ ~ ~ ~ ~ ~ ~ ~ 60% สำหรับ Skylake หากคุณไม่ได้ปรับแต่งการตั้งค่าการจัดการพลังงาน)


สิ่งที่ดีที่สุดที่คุณสามารถทำได้ด้วย zlib ที่กำหนดเองด้วยskipbytesฟังก์ชั่นคือการแยกสัญลักษณ์ในบล็อกการบีบอัดเพื่อไปยังจุดสิ้นสุดโดยไม่ต้องทำงานเพื่อสร้างบล็อกที่คลายการบีบอัดใหม่ นี่อาจจะเร็วกว่าอย่างมาก (อาจเป็นอย่างน้อย 2x) กว่าการเรียกฟังก์ชันถอดรหัสปกติของ zlib เพื่อเขียนทับบัฟเฟอร์เดิมและเลื่อนไปข้างหน้าในไฟล์ แต่ฉันไม่รู้ว่ามีใครเขียนฟังก์ชันดังกล่าวหรือไม่ (และฉันคิดว่ามันใช้งานไม่ได้จริงยกเว้นว่าไฟล์นั้นถูกเขียนขึ้นเป็นพิเศษเพื่อให้ตัวถอดรหัสสามารถเริ่มการทำงานใหม่ได้ที่บล็อกบางอัน)

ผมก็หวังว่าไม่มีทางที่จะข้ามผ่านบล็อกยุบโดยไม่ต้องถอดรหัสพวกเขาเพราะที่จะมากได้เร็วขึ้น ต้นไม้ Huffman ถูกส่งไปที่จุดเริ่มต้นของแต่ละบล็อกเพื่อให้คุณสามารถถอดรหัสจากจุดเริ่มต้นของบล็อกใด ๆ (ฉันคิดว่า) โอ้ฉันคิดว่าสถานะถอดรหัสเป็นมากกว่าต้นไม้ Huffman แต่ก็เป็นข้อมูลถอดรหัสที่ 32kiB ก่อนหน้าและนี่ไม่ได้ถูกรีเซ็ต / ลืมข้ามขอบเขตบล็อกโดยค่าเริ่มต้น ไบต์เดียวกันสามารถถูกอ้างอิงซ้ำ ๆ ได้ดังนั้นอาจปรากฏเพียงครั้งเดียวในไฟล์บีบอัดขนาดยักษ์ (เช่นในไฟล์บันทึกชื่อโฮสต์อาจยังคง "ร้อน" ในพจนานุกรมการบีบอัดตลอดเวลาและทุกอินสแตนซ์ของมันอ้างอิงหนึ่งก่อนหน้านี้ไม่ใช่ชื่อแรก)

zlibคู่มือบอกว่าคุณต้องใช้Z_FULL_FLUSHเมื่อโทรdeflateถ้าคุณต้องการกระแสบีบอัดที่จะ seekable ไปยังจุดที่ มัน "รีเซ็ตสถานะการบีบอัด" ดังนั้นฉันคิดว่าไม่มีการอ้างอิงย้อนหลังสามารถเข้าไปในบล็อกก่อนหน้า ดังนั้นหากไฟล์ zip ของคุณถูกเขียนด้วยบล็อกเต็มเปี่ยมเป็นครั้งคราว (เช่นทุก 1G หรือบางสิ่งบางอย่างอาจส่งผลกระทบต่อการบีบอัดข้อมูลเล็กน้อย) ฉันคิดว่าคุณจะต้องทำงานถอดรหัสให้มากขึ้นกว่าที่คุณต้องการในตอนแรก ความคิด ฉันเดาว่าคุณคงไม่สามารถเริ่มต้นเมื่อเริ่มต้นบล็อกใดก็ได้


ส่วนที่เหลือถูกเขียนในขณะที่ฉันคิดว่ามันจะเป็นไปได้ที่จะหาจุดเริ่มต้นของบล็อกที่มีไบต์แรกที่คุณต้องการและถอดรหัสจากที่นั่น

แต่น่าเสียดายที่การเริ่มต้นบล็อก Deflate ไม่ได้ระบุระยะเวลาของบล็อกบีบอัด ข้อมูลที่ไม่สามารถบีบอัดสามารถเข้ารหัสด้วยประเภทบล็อกที่ไม่บีบอัดที่มีขนาด 16 บิตเป็นไบต์ที่ด้านหน้า แต่บล็อกที่บีบอัดไม่ได้: RFC 1951 อธิบายรูปแบบที่อ่านได้ง่าย บล็อกที่มีการเข้ารหัส Huffman แบบไดนามิกมีต้นไม้ที่ด้านหน้าของบล็อก (ดังนั้นตัวขยายการบีบอัดไม่จำเป็นต้องค้นหาในสตรีม) ดังนั้นคอมเพรสเซอร์จะต้องเก็บบล็อกทั้งหมด (บีบอัด) ไว้ในหน่วยความจำก่อนที่จะเขียน

ระยะอ้างอิงย้อนหลังสูงสุดเพียง 32kiB ดังนั้นคอมเพรสเซอร์จึงไม่จำเป็นต้องเก็บข้อมูลที่ไม่ได้บีบอัดไว้ในหน่วยความจำมากนัก แต่นั่นไม่ได้ จำกัด ขนาดบล็อก บล็อกสามารถมีความยาวหลายเมกะไบต์ (นี่มีขนาดใหญ่พอสำหรับดิสก์ที่ค้นหาว่ามีค่าแม้จะอยู่บนไดรฟ์แบบแม่เหล็กเทียบกับการอ่านตามลำดับลงในหน่วยความจำและเพียงข้ามข้อมูลใน RAM ถ้าเป็นไปได้ที่จะหาจุดสิ้นสุดของบล็อกปัจจุบันโดยไม่ต้องแยกวิเคราะห์)

zlib สร้างบล็อกให้นานที่สุด: ตามที่ Marc Adler , zlib จะเริ่มบล็อกใหม่เมื่อบัฟเฟอร์สัญลักษณ์เต็มขึ้นซึ่งการตั้งค่าเริ่มต้นคือสัญลักษณ์ 16,383 (ตัวอักษรหรือไม้ขีดไฟ)


ฉันส่งเอาต์พุตของseq(ซึ่งซ้ำซ้อนอย่างมากและอาจไม่ใช่การทดสอบที่ยอดเยี่ยม) แต่มีการpv < /tmp/seq1G.gz | gzip -d | tail -c $((1024*1024*1000)) | wc -cทำงานที่เพียง 62 62 MiB / s ของข้อมูลที่ถูกบีบอัดบน Skylake i7-6700k ที่ 3.9GHz พร้อม DDR4-2666 RAM นั่นคือ 246MiB / s ของข้อมูลที่ถูกแตกซึ่งเป็นการเปลี่ยนแปลงของก้อนเมื่อเทียบกับmemcpyความเร็วของ ~ 12 GiB / s สำหรับขนาดบล็อกที่ใหญ่เกินไปที่จะใส่ในแคช

(ด้วยการenergy_performance_preferenceตั้งค่าเป็นค่าเริ่มต้นbalance_powerแทนที่จะbalance_performanceเป็นผู้ว่าราชการซีพียูภายในของ Skylake ตัดสินใจที่จะทำงานที่ 2.7GHz เท่านั้น ~ 43 MiB / s ของข้อมูลที่ถูกบีบอัดฉันใช้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ถูกผูกไว้กับ CPU แม้กระทั่งบน CPU ที่เร็วเว้นแต่คุณจะมีดิสก์ที่ช้ามาก gzip ใช้ CPU 100% ที่รันบน (และรัน 1.81 คำสั่งต่อนาฬิกาอ้างอิงจากperf) และtailใช้ 0.162 ของ CPU ที่รันบน (0.58 IPC) ระบบไม่ได้ใช้งานเป็นส่วนใหญ่

ฉันใช้ Linux 4.14.11-1-ARCH ซึ่งเปิดใช้งาน KPTI เป็นค่าเริ่มต้นเพื่อแก้ไข Meltdown ดังนั้นการwriteเรียกใช้ระบบเหล่านั้นทั้งหมดจึงgzipมีราคาแพงกว่าที่เคยเป็น: /


การมีการค้นหาในตัวunzipหรือzcat(แต่ยังคงใช้zlibฟังก์ชั่นถอดรหัสปกติ)จะช่วยประหยัดการเขียนไปป์ทั้งหมดและจะทำให้ซีพียู Skylake ทำงานที่ความเร็วสัญญาณนาฬิกาเต็ม (การดาวน์โหลดสำหรับการโหลดบางประเภทนั้นไม่เหมือนกันสำหรับ Intel Skylake และใหม่กว่าซึ่งมีการลดความถี่การตัดสินใจของ CPU ที่ทำจากระบบปฏิบัติการเนื่องจากพวกเขามีข้อมูลเพิ่มเติมเกี่ยวกับสิ่งที่ CPU กำลังทำอยู่และสามารถเพิ่ม / ลดความเร็วได้เร็วขึ้น ปกติดี แต่ที่นี่นำไปสู่การ Skylake ไม่เร่งความเร็วเต็มที่กับการตั้งค่าผู้ว่าการอนุรักษ์มากขึ้น)

ไม่มีการเรียกระบบเพียงแค่เขียนบัฟเฟอร์ที่เหมาะกับแคช L2 จนกว่าคุณจะไปถึงตำแหน่งไบต์เริ่มต้นที่คุณต้องการอาจสร้างความแตกต่าง% อย่างน้อย อาจจะเป็น 10% แต่ฉันแค่คิดเลขตรงนี้ ฉันยังไม่ได้ทำโปรไฟล์zlibในรายละเอียดใด ๆ เพื่อดูว่ามีแคชของรอยเท้าขนาดใหญ่เท่าใดและ TLB flush (และ uop-cache flush) ในการโทรของระบบทุกครั้งที่เปิดใช้งาน KPTI


มีโครงการซอฟต์แวร์ไม่กี่รายที่จะเพิ่มแสวงหาดัชนีรูปแบบไฟล์ gzip มี นี่ไม่ได้ช่วยอะไรคุณถ้าคุณไม่สามารถให้ใครสร้างไฟล์บีบอัดที่หาได้ให้คุณ แต่ผู้อ่านคนอื่น ๆ ในอนาคตอาจได้รับประโยชน์

สันนิษฐานว่าค่าของโครงการเหล่านี้มีฟังก์ชั่นการถอดรหัสที่รู้วิธีการข้ามผ่านกระแสยุบโดยไม่ต้องดัชนีเพราะพวกเขากำลังออกแบบเฉพาะเพื่อการทำงานเมื่อดัชนีเป็นใช้ได้

  • GZinga: Gzip ที่ค้นหาและแยกได้ ช่วยให้บล็อกมีขนาดใหญ่
  • BGZF - บล็อกใหญ่กว่า & ดีกว่า GZIP! (ขนาดบล็อกสูงสุดเล็ก ๆ = 64kiB เจ็บอัตราส่วนการบีบอัดเล็กน้อยออกแบบมาเพื่อใช้กับข้อมูลชีวสารสนเทศเช่น FASTA ที่มักจะใช้การบีบอัดด้วยการสนับสนุนที่โปร่งใสในห้องสมุดหลามบางส่วน)

1

คุณสามารถเปิดไฟล์ zip ในเซสชัน python โดยใช้zf = zipfile.ZipFile(filename, 'r', allowZip64=True)และเมื่อเปิดคุณสามารถเปิดอ่านไฟล์ใดก็ได้ภายในไฟล์ zip และอ่านบรรทัดเป็นต้นจากไฟล์นั้นราวกับว่าเป็นไฟล์ปกติ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.