แทนที่สตริงที่มีการขึ้นบรรทัดใหม่ในไฟล์ขนาดใหญ่


16

มีใครรู้บ้างเกี่ยวกับเครื่องมือที่ไม่ใช้บรรทัดในการค้นหา / แทนที่สตริงในวิธีที่ค่อนข้างมีประสิทธิภาพ ดูคำถามนี้ด้วย

ฉันมีไฟล์ข้อความ + 2GB ที่ฉันต้องการประมวลผลคล้ายกับสิ่งที่ดูเหมือนจะทำ:

sed -e 's/>\n/>/g'

ซึ่งหมายความว่าผมต้องการที่จะลบบรรทัดใหม่ทั้งหมดที่เกิดขึ้นหลังจากที่มีแต่ไม่ได้ทุกที่อื่นดังนั้นกฎที่ออกมา>tr -d

คำสั่งนี้ (ที่ฉันได้จากคำตอบของคำถามที่คล้ายกัน ) ล้มเหลวด้วยcouldn't re-allocate memory:

sed --unbuffered ':a;N;$!ba;s/>\n/>/g'

ดังนั้นมีวิธีการอื่นใดโดยไม่หันไปใช้ C? ฉันเกลียด Perl แต่ยินดีที่จะยกเว้นในกรณีนี้ :-)

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

มีความคิดที่ดีมีใครบ้าง?


คุณลองตัวเลือกแล้ว--unbufferedหรือยัง
ctrl-alt-delor

มีหรือไม่มี--unbufferedหน่วยความจำไม่
เพียงพอ

อะไร$!ทำอย่างไร
ctrl-alt-delor

เกิดอะไรขึ้นกับคำสั่ง sed แรก อย่างที่สองดูเหมือนว่าจะอ่านทุกอย่างในพื้นที่รูปแบบฉันไม่รู้ว่า$!เป็น ฉันคาดหวังว่าจะต้องมีหน่วยความจำจำนวนมาก
ctrl-alt-delor

ปัญหาคือว่า sed อ่านทุกอย่างเป็นบรรทัดนั่นเป็นสาเหตุที่คำสั่งแรกไม่ลบบรรทัดใหม่เนื่องจากมันจะแสดงผลข้อความทีละแถวอีกครั้ง คำสั่งที่สองเป็นเพียงวิธีแก้ปัญหา ฉันคิดว่าsedไม่ใช่เครื่องมือที่เหมาะสมในกรณีนี้
MattBianco

คำตอบ:


14

นี่เป็นเรื่องไม่สำคัญใน Perl คุณไม่ควรเกลียดมัน!

perl -i.bak -pe 's/>\n/>/' file

คำอธิบาย

  • -i: file.bakแก้ไขไฟล์ในสถานที่และสร้างการสำรองข้อมูลของเดิมที่เรียกว่า หากคุณไม่ต้องการสำรองข้อมูลให้ใช้perl -i -peแทน
  • -pe-eอ่านบรรทัดแฟ้มใส่โดยสายและพิมพ์แต่ละบรรทัดหลังจากการใช้สคริปต์ที่ได้รับเป็น
  • s/>\n/>/: sedการเปลี่ยนตัวผู้เล่นเช่นเดียวกับ

และนี่คือawkวิธีการ:

awk  '{if(/>$/){printf "%s",$0}else{print}}' file2 

3
+1 awk golf:awk '{ORS=/>$/?"":"\n"}1'
glenn jackman

1
ทำไมฉันไม่ชอบ perl ทั่วไปเป็นเหตุผลเดียวกันกับที่ฉันเลือกคำตอบนี้ (หรือความคิดเห็นของคุณต่อคำตอบของ Gnouc): ความสามารถในการอ่าน การใช้ Perl -pe กับ "รูปแบบ sed" ง่าย ๆ เป็นวิธีที่อ่านง่ายกว่าการแสดงออกที่ซับซ้อน
MattBianco

3
@ MattBianco ยุติธรรมเพียงพอ แต่เพียงเพื่อให้คุณรู้ว่าไม่มีส่วนเกี่ยวข้องกับ Perl ลักษณะที่ Gnouc ใช้เป็นคุณสมบัติของภาษาการแสดงออกปกติบางอย่าง (รวมถึง แต่ไม่ จำกัด เพียง PCREs) ไม่ใช่ความผิดของ Perl เลย นอกจากนี้หลังจากที่แสดงความประหลาดใจ':a;N;$!ba;s/>\n/>/g'ในคำถามของคุณคุณสละสิทธิ์ที่จะบ่นเกี่ยวกับความสามารถในการอ่าน! : P
terdon

@glennjackman ดีมาก! ฉันเล่นกับสิ่งfoo ? bar : bazปลูกสร้าง แต่ไม่สามารถใช้งานได้
terdon

@terdon: Yeap ความผิดพลาดของฉัน ลบมัน.
cuonglm

7

perlวิธีการแก้ปัญหา:

$ perl -pe 's/(?<=>)\n//'

ชี้แจง

  • s/// ใช้สำหรับการทดแทนสตริง
  • (?<=>) เป็นรูปแบบ lookbehind
  • \n ตรงกับบรรทัดใหม่

ความหมายของรูปแบบทั้งหมดจะลบบรรทัดใหม่ทั้งหมดที่มีอยู่>ก่อนหน้า


2
สนใจที่จะแสดงความคิดเห็นว่าส่วนต่างๆของโปรแกรมทำอะไร ฉันมักจะมองหาที่จะเรียนรู้
MattBianco

2
ทำไมต้องกังวลกับลุคที่อยู่เบื้องหลัง? ทำไมไม่เพียงs/>\n/>/?
terdon

1
หรือs/>\K\n//จะทำงานด้วย
เกล็นแจ็คแมน

@terdon: สิ่งแรกที่ฉันคิดเอาออกแทนแทนที่
cuonglm

@glennjackman: จุดดี!
cuonglm

3

เกี่ยวกับสิ่งนี้:

sed ':loop
  />$/ { N
    s/\n//
    b loop
  }' file

สำหรับ GNU sed คุณสามารถลองเพิ่มตัวเลือก-u( --unbuffered) ตามคำถามได้ GNU sed ยังมีความสุขกับสิ่งนี้ในฐานะหนึ่งซับง่าย:

sed ':loop />$/ { N; s/\n//; b loop }' file

ไม่ได้ลบไฟล์สุดท้าย\nหากไฟล์จบลง>\nแต่อาจเป็นวิธีที่ดีกว่า
Stéphane Chazelas

@ StéphaneChazelasเหตุใดการปิด}จึงจำเป็นต้องอยู่ในนิพจน์แยกกัน สิ่งนี้จะไม่ทำงานในรูปแบบหลายบรรทัดหรือไม่
แกรม

1
ที่จะทำงานใน POSIX seds ด้วยb loop\n}หรือ-e 'b loop' -e '}'ไม่b loop;}และไม่แน่นอนb loop}เพราะ}และ;ใช้ได้ในชื่อฉลาก (แม้ว่าไม่มีใครในใจที่ถูกต้องจะใช้มันและนั่นหมายความว่า GNU sed ไม่ใช่ POSIX ที่สอดคล้องกัน) และ}คำสั่งจะต้องแยกจากกัน จากbคำสั่ง
Stéphane Chazelas

@ StéphaneChazelas, GNU sedมีความสุขกับทุกสิ่งที่กล่าวมาแม้จะมี--posix! มาตรฐานยังมีต่อไปนี้สำหรับการแสดงออกรั้ง The list of sed functions shall be surrounded by braces and separated by <newline>s- นี่ไม่ได้หมายความว่าควรใช้เครื่องหมายอัฒภาคนอกวงเล็บเท่านั้นหรือไม่
แกรม

@mikeserv จำเป็นต้องใช้การวนรอบเพื่อจัดการบรรทัดที่ลงท้าย>ด้วย ต้นฉบับไม่เคยมีหนึ่งนี้ถูกชี้ให้เห็นโดยStéphane
แกรม

1

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

sed ':a;$!N;s/>\n/>/;P;D;ba'

แก้ไข: หลังจากอ่านOne-Liners ชื่อดังของ Peteris Kruminsอีกครั้งฉันเชื่อว่าsedทางออกที่ดีกว่าน่าจะเป็น

sed -e :a -e '/>$/N; s/\n//; ta'

ซึ่งจะต่อท้ายบรรทัดต่อไปนี้ในกรณีที่มีการ>จับคู่แล้วในตอนท้ายและควรวนกลับไปตามเงื่อนไขเพื่อจัดการกับกรณีของการจับคู่บรรทัดที่ต่อเนื่องกัน (เป็น39ของ Krumin ต่อท้ายบรรทัดต่อไป "\"อย่างแน่นอนยกเว้นการแทนที่>สำหรับ\เป็นอักขระการรวมและความจริงที่ว่าอักขระการรวมถูกเก็บรักษาไว้ในผลลัพธ์)


2
ที่ไม่ทำงานถ้าติดต่อกัน 2 สายสิ้นสุดใน>(ที่ยัง GNU เฉพาะ)
Stéphane Chazelas

1

sedไม่ได้มีวิธีในการส่งออกโดยไม่ต้องขึ้นบรรทัดใหม่ขั้นสุดท้าย วิธีการของคุณที่ใช้Nงานพื้นฐาน แต่เก็บเส้นที่ไม่สมบูรณ์ไว้ในหน่วยความจำและอาจล้มเหลวได้หากบรรทัดยาวเกินไป (โดยทั่วไปแล้วการฝังตัวที่ไม่ได้ออกแบบมาเพื่อจัดการกับบรรทัดที่ยาวมาก)

คุณสามารถใช้ awk แทน

awk '{if (/<$/) printf "%s", $0; else print}'

อีกวิธีหนึ่งคือการใช้trเพื่อสลับอักขระขึ้นบรรทัดใหม่ด้วยอักขระ“ น่าเบื่อ” ที่เกิดขึ้นบ่อยครั้ง Space อาจใช้งานได้ที่นี่ - เลือกอักขระที่มีแนวโน้มที่จะปรากฏในทุกบรรทัดหรืออย่างน้อยก็เป็นสัดส่วนจำนวนมากในข้อมูลของคุณ

tr ' \n' '\n ' | sed 's/> />/g' | tr '\n ' ' \n'

ทั้งสองวิธีแสดงให้เห็นแล้วที่นี่เพื่อให้ได้ผลที่ดีขึ้นในคำตอบอื่น ๆ และวิธีการของเขาด้วยsedไม่ทำงานโดยไม่มีบัฟเฟอร์ 2.5 กิกะไบต์
mikeserv

มีใครพูดถึง awk บ้างไหม? โอ้ฉันไม่ได้ทำฉันสังเกตเห็นเพียงคำตอบของ perdon ใน terdon ด้วยเหตุผลบางอย่าง ไม่มีใครกล่าวถึงtrวิธีการ - mikeserv คุณโพสต์ที่แตกต่างกัน (ถูกต้อง แต่ทั่วไปน้อยกว่า) trวิธีการที่เกิดขึ้นนอกจากนี้ยังมีการใช้งาน
Gilles 'หยุดความชั่วร้าย'

ใช้ได้จริง แต่เสียงสามัญน้อยกว่าสำหรับฉันอย่างที่คุณเคยเรียกมันว่าเป็นโซลูชันที่ทำงานได้ตามเป้าหมาย ฉันคิดว่ามันยากที่จะยืนยันว่าสิ่งนั้นไม่มีประโยชน์ซึ่งแปลกเพราะมันมี 0 upvotes ความแตกต่างที่ยิ่งใหญ่ที่สุดของฉันสามารถดูระหว่างการแก้ปัญหาของตัวเองและของคุณทั่วไปมากขึ้นเสนอขายเป็นที่เหมืองเฉพาะแก้ปัญหาในขณะที่คุณอาจทั่วไป นั่นอาจทำให้คุ้มค่า - และฉันอาจย้อนกลับการลงคะแนนของฉัน - แต่ยังมีเรื่องน่ารำคาญของ 7 ชั่วโมงระหว่างพวกเขาและรูปแบบที่เกิดขึ้นซ้ำของคำตอบของคุณเลียนแบบคนอื่น ๆ คุณอธิบายได้ไหม
mikeserv



-1

มีหลายวิธีในการทำเช่นนี้และส่วนใหญ่ที่นี่ดีจริงๆ แต่ฉันคิดว่านี่คือสิ่งที่ฉันชอบ:

tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'

หรือแม้กระทั่ง:

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

ฉันไม่สามารถรับคำตอบแรกให้ทำงานได้เลย *ในขณะที่ผมชื่นชมความสง่างามของคนที่สองที่ผมเชื่อว่าคุณต้องลบ วิธีการก็คือตอนนี้ก็จะลบบรรทัดว่างใด ๆ >ต่อไปนี้สายที่ลงท้ายด้วย …อืม เมื่อมองกลับไปที่คำถามฉันเห็นว่ามันคลุมเครือเล็กน้อย คำถามกล่าวว่า“ ฉันต้องการลบบรรทัดใหม่ทั้งหมดที่เกิดขึ้นหลังจาก>, …” ฉันตีความว่าหมายถึง>\n\n\n\n\nfooควรเปลี่ยนเป็น\n\n\n\nfooแต่ฉันคิดว่า fooอาจเป็นผลลัพธ์ที่ต้องการ
สกอตต์

@Scott - ฉันทดสอบกับรูปแบบต่าง ๆ ต่อไปนี้: printf '>\n>\n\n>>\n>\n>>>\n>\nf\n\nff\n>\n' | tr '>\n' '\n>' | sed 's/^>*//;H;/./!d;x;y/\n>/>\n/'- นั่นทำให้>>>>>>>>>>f\n\nff\n\nฉันได้รับคำตอบแรก ฉันอยากรู้ว่าสิ่งที่คุณทำเพื่อทำลายมันเพราะฉันต้องการแก้ไข สำหรับประเด็นที่สอง - ฉันไม่เห็นด้วยว่ามันคลุมเครือ สหกรณ์ไม่ได้ขอให้ลบทั้งหมด > ก่อน\n ewline แต่จะลบทุก \n ewlines ต่อไป >
mikeserv

1
ใช่ แต่การตีความที่ถูกต้องคือใน>\n\n\n\n\nเฉพาะ newline แรกเท่านั้นหลังจาก>; คนอื่น ๆ ทั้งหมดกำลังติดตาม newline อื่น ๆ โปรดทราบว่า OP ของ“นี่คือสิ่งที่ฉันต้องการถ้าเพียง แต่มันทำงาน” ข้อเสนอแนะก็ไม่ได้sed -e 's/>\n/>/g' sed -e 's/>\n*/>/g'
สกอตต์

1
@Scott - ข้อเสนอแนะไม่ทำงานและไม่สามารถทำได้ ฉันไม่เชื่อว่าคำแนะนำเกี่ยวกับรหัสของคนที่ไม่เข้าใจรหัสถือเป็นจุดตีความที่ถูกต้องเนื่องจากภาษาธรรมดาที่บุคคลนั้นใช้ และนอกจากนี้ผลลัพธ์ - ถ้าใช้งานได้จริง - จากs/>\n/>/บน>\n\n\n\n\nจะยังคงเป็นสิ่งที่s/>\n/>/จะแก้ไข
mikeserv
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.