เครื่องมือที่ไม่ใช่เชิงเส้นสำหรับการเปลี่ยนสตริง?


13

ฉันเพิ่งถามคำถามเกี่ยวกับวิธีการลบอักขระบรรทัดใหม่หากเกิดขึ้นหลังจากอักขระเฉพาะอื่น

เครื่องมือประมวลผลข้อความ Unix นั้นมีประสิทธิภาพมาก แต่เกือบทั้งหมดจะจัดการกับบรรทัดของข้อความ

แต่ฉันควรทำอย่างไรหากฉันต้องการแทนที่ลำดับข้อความในไฟล์ขนาดใหญ่ที่ไม่มีบรรทัดใหม่

ตัวอย่างเช่นแทนที่<foobar>ด้วย\n<foobar>โดยไม่ต้องอ่านอินพุตบรรทัดต่อบรรทัด? (เนื่องจากมีเพียงหนึ่งบรรทัดและมีความยาวอักขระ 2.5G)


1
คุณเปิดให้ใช้perlหรือpythonไม่?
iruvar

Perl เป็นเรื่องปกติ ฉันเพิ่งพบgsar( home.online.no/~tjaberg ) ซึ่งฉันจะลอง
MattBianco

คำตอบ:


12

สิ่งแรกที่เกิดขึ้นกับฉันเมื่อเผชิญกับปัญหาประเภทนี้คือการเปลี่ยนตัวแยกบันทึก ในเครื่องมือส่วนใหญ่จะถูกตั้งค่าเป็นค่า\nเริ่มต้น แต่สามารถเปลี่ยนแปลงได้ ตัวอย่างเช่น:

  1. Perl

    perl -0x3E -pe 's/<foobar>/\n$&/' file
    

    คำอธิบาย

    • -0: ชุดนี้คั่นบันทึกการป้อนข้อมูลให้กับตัวละครที่ได้รับมันคุ้มค่าเลขฐานสิบหก ในกรณีนี้ผมกำลังตั้งค่าให้มีค่าเป็นฐานสิบหก> รูปแบบทั่วไปคือ3E -0xHEX_VALUEนี่เป็นเพียงเคล็ดลับในการแบ่งสายออกเป็นกลุ่มที่จัดการได้
    • -pe: -eพิมพ์สายการป้อนข้อมูลแต่ละหลังจากการใช้สคริปต์ที่กำหนดโดย
    • s/<foobar>/\n$&/: การทดแทนอย่างง่าย มีสิ่งที่ถูกจับคู่ในกรณีนี้$&<foobar>
  2. awk

    awk '{gsub(/foobar>/,"\n<foobar>");printf "%s",$0};' RS="<" file
    

    คำอธิบาย

    • RS="<": >สร้างสถิติการป้อนข้อมูลเพื่อคั่น
    • gsub(/foobar>/,"\n<foobar>"): ทดแทนทุกกรณีของการที่มีfoobar> \n<foobar>โปรดทราบว่าเนื่องจากRSมีการตั้งค่า<ทั้งหมด<จะถูกลบออกจากแฟ้มใส่ (นั่นเป็นวิธีที่awkใช้ได้) ดังนั้นเราจึงจำเป็นต้องตรงกับfoobar>(โดยไม่ต้อง<) \n<foobar>และแทนที่ด้วย
    • printf "%s",$0: พิมพ์ "บรรทัด" ปัจจุบันหลังจากการแทนที่ $0เป็นบันทึกในปัจจุบันดังนั้นจึงจะถือเป็นสิ่งที่ก่อนawk<

ฉันทดสอบสิ่งเหล่านี้ใน 2.3 GB ไฟล์บรรทัดเดียวที่สร้างขึ้นด้วยคำสั่งเหล่านี้:

for i in {1..900000}; do printf "blah blah <foobar>blah blah"; done > file
for i in {1..100}; do cat file >> file1; done
mv file1 file

ทั้งสองawkและperlใช้ในปริมาณเล็กน้อยของหน่วยความจำ


คุณเคยลองTie::File perldoc.perl.org/Tie/File.htmlไหม ฉันคิดว่ามันเป็นคุณสมบัติที่ดีที่สุดPerlเมื่อจัดการกับไฟล์ขนาดใหญ่
cuonglm

@Gnouc ฉันได้เล่นกับมันเล็กน้อยใช่ แต่ฉัน) OP ได้ทราบแล้วว่าไม่ชอบ Perl ในคำถามอื่นดังนั้นฉันจึงต้องการให้มันง่าย ii) ฉันมักจะหลีกเลี่ยงการใช้โมดูลภายนอกเว้นแต่จำเป็นจริงๆและ iii) การใช้โมดูล Tie :: File จะทำให้ไวยากรณ์น้อยลงมาก ชัดเจน.
terdon

ตกลง. บันทึกเล็ก ๆ น้อย ๆ ที่เป็นโมดูลหลักตั้งแต่Tie::File v5.7.3
cuonglm

9

gsar (การค้นหาและแทนที่ทั่วไป)เป็นเครื่องมือที่มีประโยชน์มากสำหรับวัตถุประสงค์นี้

คำตอบส่วนใหญ่สำหรับคำถามนี้ใช้เครื่องมือที่ใช้บันทึกและเทคนิคต่าง ๆ เพื่อปรับให้เข้ากับปัญหาเช่นการเปลี่ยนอักขระตัวคั่นเรคคอร์ดเริ่มต้นเป็นสิ่งที่สันนิษฐานว่าเกิดขึ้นบ่อยครั้งพอในอินพุตไม่ทำให้แต่ละระเบียนมีขนาดใหญ่เกินกว่าจะจัดการได้

ในหลายกรณีนี้ดีมากและสามารถอ่านได้ ฉันทำเช่นเดียวกับปัญหาที่สามารถได้อย่างง่ายดาย / แก้ไขได้อย่างมีประสิทธิภาพด้วยเครื่องมือที่มีอยู่ทุกที่เช่นawk, tr, sedและเปลือกบอร์น

ทำการค้นหาแบบไบนารีและแทนที่ในไฟล์ขนาดใหญ่ที่มีเนื้อหาสุ่มไม่เหมาะสำหรับเครื่องมือ unix มาตรฐานเหล่านี้

คุณบางคนอาจคิดว่านี่เป็นการโกง แต่ฉันไม่เห็นว่าการใช้เครื่องมือที่เหมาะสมสำหรับงานอาจผิด ในกรณีนี้มันเป็นโปรแกรมที่เรียกว่า C gsarที่ได้รับอนุญาตภายใต้GPL v2ดังนั้นมันน่าประหลาดใจฉันไม่น้อยว่ามีแพคเกจสำหรับเครื่องมือนี้มีประโยชน์มากในค่าไม่มีGentoo , RedHatหรืออูบุนตู

gsarใช้เป็นตัวแปรไบนารีของวิธีการค้นหาสตริงบอยเยอร์มัวร์

การใช้งานตรงไปตรงมา:

gsar -F '-s<foobar>' '-r:x0A<foobar>'

ที่-Fหมายถึง "กรอง" โหมดคืออ่านเขียนไปstdin stdoutมีวิธีการใช้งานไฟล์เช่นกัน -sระบุสตริงการค้นหาและ-rการแทนที่ เครื่องหมายโคลอนสามารถใช้เพื่อระบุค่าไบต์ที่กำหนดเอง

รองรับโหมดตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ ( -i) แต่ไม่มีการรองรับนิพจน์ทั่วไปเนื่องจากอัลกอริทึมใช้ความยาวของสตริงการค้นหาเพื่อปรับการค้นหาให้เหมาะสม

เครื่องมือนี้สามารถใช้สำหรับการค้นหาได้เช่นgrepกัน gsar -bเอาท์พุทชดเชยไบต์ของสตริงการค้นหาตรงและgsar -lพิมพ์ชื่อไฟล์และจำนวนของการแข่งขันถ้ามีบิตเช่นการรวมกับgrep -lwc

เครื่องมือนี้เขียนขึ้นโดยTormod Tjaberg (เริ่มต้น) และHans Peter Verne (ปรับปรุง)


ถ้าเป็น GPL คุณจะลองพิจารณาบรรจุมันให้เป็น distro :)
Rqomey

1
อันที่จริงฉันคิดอย่างจริงจังเกี่ยวกับการสร้าง gentoo ebuild อาจเป็นรอบต่อนาทีเช่นกัน แต่ฉันไม่เคยสร้างแพคเกจ. deb มาก่อนดังนั้นฉันหวังว่าจะมีคนเอาชนะฉัน (เพราะจะใช้เวลาสักครู่)
MattBianco

ฉันสงสัยนี้เป็นชมเชยมาก แต่ homebrew OS X gsarมีสูตรสำหรับ
crazysim

5

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

นี่คือตัวอย่างของ Python ที่แทนที่foobarด้วยXXXXXX

#! /usr/bin/python
import mmap
import contextlib   
with open('test.file', 'r+') as f:
 with contextlib.closing(mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_WRITE)) as m:
   pos = 0
   pos = m.find('foobar', pos)
   while pos > 0:
    m[pos: pos+len('XXXXXX')] = 'XXXXXX'
    pos = m.find('foobar', pos)

4

มีเครื่องมือมากมายสำหรับสิ่งนี้:

ddคือสิ่งที่คุณต้องการใช้หากคุณต้องการปิดกั้นไฟล์ - อ่านได้อย่างน่าเชื่อถือจำนวนไบต์ที่แน่นอนเท่านั้นในบางครั้ง มันจัดการการบล็อกและการยกเลิกการบล็อกกระแสไฟล์แบบพกพา:

tr -dc '[:graph:]' </dev/urandom | dd bs=32 count=1 cbs=8 conv=unblock,sync 2>/dev/null

###OUTPUT###

UI(#Q5\e BKX2?A:Z RAxGm:qv t!;/v!)N

ฉันยังใช้trด้านบนเนื่องจากสามารถจัดการการแปลง ASCII ใด ๆ เป็นอื่น ๆ (หรือในกรณีนี้การลบไบต์ ASCII ใด ๆ ที่ไม่ใช่อักขระที่ไม่สามารถพิมพ์ได้) เป็นสิ่งที่ฉันใช้ในการตอบคำถามอื่นของคุณเมื่อเช้านี้จริง ๆ แล้วเมื่อฉัน:

tr '>\n' '\n>' | sed 's/^>*//' | tr '\n>' '>\n' 

มีหลายคนที่คล้ายกัน รายการดังกล่าวควรมีชุดย่อยของตัวหารร่วมที่ต่ำที่สุดซึ่งคุณอาจคุ้นเคย

แต่ถ้าผมจะทำการประมวลผลข้อความบน 2.5gbs odของไฟล์ไบนารีผมอาจจะเริ่มต้นด้วย สามารถให้octal dumpรูปแบบอื่น ๆ หรือหลายรูปแบบ คุณสามารถระบุตัวเลือกทุกประเภท - แต่ฉันจะทำเพียงหนึ่งไบต์ต่อบรรทัดใน\Cรูปแบบที่หลีกเลี่ยง:

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

printf 'first\nnewline\ttab spacefoobar\0null' |
od -A n -t c -v -w1 |
sed 's/^ \{1,3\}//;s/\\$/&&/;/ /bd
     /\\[0nt]/!{H;$!d};{:d
    x;s/\n//g}'

นั่นเล็กน้อยเหนือ delimits บน\newlines, \0nulls, \tabs และ<spaces>ในขณะที่การรักษา\Cสตริงหนีสำหรับตัวคั่น จดบันทึกHและxใช้ฟังก์ชั่น - ทุกครั้งที่sedพบตัวคั่นมันจะสลับเนื้อหาของบัฟเฟอร์หน่วยความจำออก ด้วยวิธีนี้sedจะเก็บข้อมูลได้มากเท่าที่จะต้องกำหนดขอบเขตไฟล์อย่างน่าเชื่อถือและไม่ยอมให้บัฟเฟอร์โอเวอร์รัน - ไม่นั่นคือตราบใดที่มันพบตัวคั่น สำหรับตราบใดที่มันไม่sedจะยังคงดำเนินการป้อนข้อมูลและจะยังคงให้มันจนกว่าจะพบodEOF

ตามที่เป็นเอาท์พุทมันจะเป็นแบบนี้:

first
\nnewline
\ttab
 spacefoobar
\0null

ดังนั้นถ้าฉันต้องการfoobar:

printf ... | od ... | sed ... | 
sed 's/foobar/\
&\
/g'

###OUTPUT###

first
\nnewline
\ttab
 space
foobar

\0null

ตอนนี้ถ้าคุณต้องการใช้ประโยชน์จากการCหลบหนีมันค่อนข้างง่าย - เพราะsedมี\\แบ็กสแลชสองเท่าแล้วจึงหลบหนีแบ็กสแลชอินพุตเดี่ยวทั้งหมดดังนั้นการดำเนินการprintfจากxargsจะไม่มีปัญหาในการสร้างเอาต์พุตตามข้อกำหนดของคุณ แต่xargs กินอัญประกาศเชลล์ดังนั้นคุณจะต้องเพิ่มราคาเป็นสองเท่า:

printf 'nl\ntab\tspace foobarfoobar\0null' |
PIPELINE |
sed 's/./\\&/g' | 
xargs printf %b | 
cat -A

###OUTPUT###

nl$
tab^Ispace $
foobar$
$
foobar$
^@null%

ที่สามารถบันทึกได้อย่างง่ายดายในตัวแปรเชลล์และส่งออกในภายหลังในรูปแบบที่เหมือนกัน สุดท้ายsedแทรก\แบ็กสแลชต่อหน้าอักขระทุกตัวในอินพุตและนั่นคือทั้งหมด

และนี่คือสิ่งที่ดูเหมือนทุกครั้งที่เคยsedมีมา:

printf 'nl\ntab\tspace foobarfoobar\0null' |
od -A n -t c -v -w1

   n
   l
  \n
   t
   a
   b
  \t
   s
   p
   a
   c
   e

   f
   o
   o
   b
   a
   r
   f
   o
   o
   b
   a
   r
  \0
   n
   u
   l
   l

2

Awk ดำเนินการบันทึกต่อเนื่อง มันสามารถใช้ตัวอักษรใด ๆ ที่เป็นตัวแยกบันทึก (ยกเว้น null ไบต์ในการใช้งานหลาย ๆ ) การใช้งานบางอย่างรองรับการแสดงออกปกติโดยพลการ (ไม่ตรงกับสตริงว่าง) เป็นตัวคั่นเร็กคอร์ด แต่สิ่งนี้อาจเทอะทะเพราะตัวแยกเร็กคอร์ดถูกตัดทอนจากจุดสิ้นสุดของแต่ละเรคคอร์ดก่อนที่จะเก็บไว้ใน$0(GNU awk ตั้งค่าตัวแปรRTเป็นตัวคั่นเรคคอร์ด ที่ถูกถอดออกจากจุดสิ้นสุดของระเบียนปัจจุบัน) โปรดทราบว่าprintยุติการส่งออกที่มีตัวคั่นบันทึกการส่งออกซึ่งเป็นบรรทัดใหม่โดยเริ่มต้นและกำหนดเป็นอิสระจากตัวคั่นบันทึกการป้อนข้อมูลORSRS

awk -v RS=, 'NR==1 {printf "input up to the first comma: %s\n", $0}'

คุณมีประสิทธิภาพสามารถเลือกตัวละครที่แตกต่างกันเป็นบันทึกแยกสำหรับเครื่องมืออื่น ๆ ( sort, sed, ... ) trโดยการแลกเปลี่ยนการขึ้นบรรทัดใหม่กับตัวละครที่มี

tr '\n,' ',\n' |
sed 's/foo/bar/' |
sort |
tr '\n,' ',\n'

ยูทิลิตี้ข้อความ GNU หลายตัวรองรับการใช้ null null แทนการขึ้นบรรทัดใหม่เป็นตัวคั่น

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