เก็บเฉพาะบรรทัดที่มีจำนวนตัวคั่นที่แน่นอน


9

ฉันมีไฟล์ csv ขนาดใหญ่ที่มี 10 ฟิลด์คั่นด้วยเครื่องหมายจุลภาค น่าเสียดายที่บางบรรทัดมีรูปแบบไม่ถูกต้องและไม่มีเครื่องหมายจุลภาค 10 รายการ (สาเหตุที่ทำให้เกิดปัญหาเมื่อฉันต้องการอ่านไฟล์ลงใน R) ฉันจะกรองเฉพาะบรรทัดที่มีเครื่องหมายจุลภาค 10 รายการได้อย่างไร


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

1
จริง ๆ แล้วมองใกล้ผู้ถามไม่ต้องการมากหรือน้อยกว่าการแข่งขัน คำถามนั้นต้องการชื่อใหม่ แต่grepคำตอบที่ไม่มีคำตอบที่ยอมรับได้สำหรับคำถามทั้ง ...
mikeserv

คำตอบ:


21

POSIX อีกอันหนึ่ง:

awk -F , 'NF == 11' <file

หากบรรทัดมีเครื่องหมายจุลภาค 10 ตัวจะมี 11 ช่องในบรรทัดนี้ ดังนั้นเราจึงawkใช้,เป็นตัวคั่นฟิลด์ ถ้าจำนวนสาขาเป็น 11 สภาพNF == 11เป็นจริงแล้วการดำเนินการดำเนินการเริ่มต้นawkprint $0


5
นั่นเป็นสิ่งแรกที่ฉันนึกถึงในคำถามนี้ ฉันคิดว่ามันเกินความจริง แต่ดูรหัส ... มันชัดเจนยิ่งขึ้น เพื่อประโยชน์ของผู้อื่น: -Fตั้งค่าตัวคั่นฟิลด์และNFอ้างอิงถึงจำนวนฟิลด์ในบรรทัดที่กำหนด เนื่องจากไม่มีการบล็อกโค้ด{statement}ต่อท้ายเงื่อนไขNF == 11การดำเนินการเริ่มต้นคือการพิมพ์บรรทัด (@cuonglm อย่าลังเลที่จะรวมคำอธิบายนี้หากคุณต้องการ)
Wildcard

4
+1: โซลูชันที่สง่างามและอ่านได้ซึ่งเป็นเรื่องทั่วไป ฉันสามารถหาบรรทัดที่มีรูปแบบผิดปกติได้ด้วยawk -F , 'NF != 11' <file
มิโรสลาฟซาโบ

@gardenhead: มันง่ายที่จะได้รับตามที่คุณเห็น OP กล่าวในความคิดเห็นของเขา บางครั้งฉันตอบจากมือถือของฉันดังนั้นจึงยากที่จะเพิ่มคำอธิบายรายละเอียด
cuonglm

1
@mikeserv: ไม่ขอโทษถ้าฉันทำให้คุณสับสนมันเป็นแค่ภาษาอังกฤษที่ไม่ดีของฉัน คุณไม่สามารถมี 11 เขตข้อมูลด้วยเครื่องหมายจุลภาค 1-9
cuonglm

1
@OlivierDulac: มันคอยปกป้องคุณจากการเริ่มต้นกับไฟล์หรือชื่อ- -
cuonglm

8

การใช้egrep(หรือgrep -Eใน POSIX):

egrep "^([^,]*,){10}[^,]*$" file.csv

สิ่งนี้จะกรองสิ่งที่ไม่มีเครื่องหมายจุลภาค 10 ตัว: ตรงกับบรรทัดเต็ม ( ^ตอนเริ่มต้นและ$ตอนท้าย) มีการซ้ำสิบครั้งอย่างแน่นอน ( {10}) ของลำดับ "จำนวนอักขระใด ๆ ยกเว้น ',' ตามด้วยซิงเกิล ','" ( ([^,]*,)) ตามด้วยอักขระจำนวนเท่าใดก็ได้ยกเว้น ',' ( [^,]*)

คุณยังสามารถใช้-xพารามิเตอร์เพื่อวางจุดยึด:

grep -xE "([^,]*,){10}[^,]*" file.csv

นี้จะมีประสิทธิภาพน้อยกว่าcuonglm 's awkวิธีการแก้ปัญหาแม้ว่า; โดยทั่วไประบบของฉันเร็วขึ้นหกครั้งสำหรับบรรทัดที่มีเครื่องหมายจุลภาคประมาณ 10 รายการ เส้นที่ยาวขึ้นจะทำให้เกิดการลดลงอย่างมาก


5

grepรหัสที่ง่ายที่สุดที่จะทำงาน:

grep -xE '([^,]*,){10}[^,]*'

คำอธิบาย:

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

-E หมายถึง "Extended regex" ซึ่งทำให้แบ็กสแลชลดการหลบหลีกใน regex ของคุณน้อยลง

วงเล็บถูกใช้สำหรับการจัดกลุ่มและ{10}หลังจากนั้นหมายความว่าจะต้องมีสิบตรงกันในแถวของรูปแบบภายใน parantheses

[^,]เป็นคลาสอักขระ - ตัวอย่างเช่น[c-f]จะจับคู่อักขระเดี่ยวใด ๆ ที่เป็นc, a d, eหรือหรือfและ[^A-Z]จะจับคู่อักขระเดี่ยวใด ๆ ที่ไม่ใช่ตัวอักษรตัวพิมพ์ใหญ่ ดังนั้น[^,]จับคู่อักขระเดี่ยวใด ๆ ยกเว้นเครื่องหมายจุลภาค

*หลังจากที่หมายถึงตัวละครคลาส "ศูนย์หรือมากกว่าของเหล่านี้."

ดังนั้นส่วน regex ([^,]*,)หมายถึง "อักขระใด ๆ ยกเว้นเครื่องหมายจุลภาคจำนวนครั้งใด ๆ (รวมถึงศูนย์ครั้ง) ตามด้วยเครื่องหมายจุลภาค" และ{10}ระบุ 10 ของเหล่านี้ จากนั้น[^,]*ให้จับคู่ส่วนที่เหลือของอักขระที่ไม่ใช่จุลภาคกับส่วนท้ายของบรรทัด


5
sed -ne's/,//11;t' -e's/,/&/10p' <in >out

บรรทัดแรกนั้นแยกออกด้วยบรรทัดใด ๆ ที่มีเครื่องหมายจุลภาค 11 รายการขึ้นไปจากนั้นพิมพ์สิ่งที่เหลืออยู่เฉพาะที่ตรงกับเครื่องหมายจุลภาค 10 รายการ

เห็นได้ชัดว่าฉันตอบคำถามนี้มาก่อน ... นี่คือการลอกเลียนแบบฉันจากคำถามที่ค้นหาสิ่งที่เกิดขึ้น 4 รูปแบบ:

คุณสามารถกำหนดเป้าหมายการ[num]เกิดรูปแบบด้วยs///คำสั่งsed ubstitution โดยเพียงเพิ่ม[num]คำสั่ง เมื่อคุณtทำการทดแทนที่ประสบความสำเร็จและไม่ได้ระบุ:ป้ายกำกับเป้าหมายtest จะแยกออกจากสคริปต์ ซึ่งหมายความว่าสิ่งที่คุณต้องทำคือทดสอบs///5เครื่องหมายจุลภาคขึ้นไปแล้วพิมพ์สิ่งที่เหลืออยู่

หรืออย่างน้อยก็จัดการเส้นที่เกินขีดสูงสุดของคุณ 4 เห็นได้ชัดว่าคุณมีข้อกำหนดขั้นต่ำ โชคดีที่ง่ายเหมือน:

sed -ne 's|,||5;t' -e 's||,|4p'

... เพียงแค่แทนที่การเกิดขึ้นครั้งที่ 4 ของ,ในบรรทัดด้วยตัวเองและตรึงrint ของคุณpเพื่อs///ธง ubstitution เนื่องจากการตัดบรรทัดที่ตรงกับ,5 ครั้งขึ้นไปถูกตัดทิ้งแล้วบรรทัดที่มี 4 ,แมตช์จึงมีเพียง 4


1
@conglm - นั่นคือสิ่งที่ฉันมีจริง ๆ ในตอนแรก แต่ผู้คนมักจะบอกฉันเสมอว่าฉันควรเขียนโค้ดที่อ่านได้มากขึ้น เนื่องจากฉันสามารถอ่านสิ่งที่คนอื่นโต้แย้งว่าฉันไม่สามารถอ่านได้ฉันไม่แน่ใจว่าจะเก็บอะไรและจะทิ้ง ... ดังนั้นฉันใส่เครื่องหมายจุลภาคที่สอง
mikeserv

@conglm - คุณสามารถเยาะเย้ยฉัน - มันจะไม่ทำร้ายความรู้สึกของฉัน ฉันตลกได้ ถ้าคุณล้อเลียนฉันมันตลกไปหน่อย มันโอเค - ฉันไม่แน่ใจและอยากรู้ ในความคิดของฉันคนควรจะหัวเราะเยาะตัวเอง อย่างไรก็ตามฉันยังไม่เข้าใจ!
mikeserv

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

มันน่าสนใจว่าในคำตอบนี้ถ้าผมแทนที่s/hello/world/2ด้วยs//world/2, GNU sed ปรับการทำงาน ด้วยสองsedจากมรดกสืบทอด/usr/5bin/posix/sedยก segfault /usr/5bin/sedเข้าสู่วง infinitive
cuonglm

@mikeserv อ้างอิงถึงการสนทนาก่อนหน้าของเราเกี่ยวกับsedและawk (ในความคิดเห็น) - ฉันชอบคำตอบนี้และยกระดับ แต่สังเกตเห็นการแปลawkคำตอบที่ยอมรับคือ: "พิมพ์บรรทัดที่มี 11 ฟิลด์" และการแปลsedคำตอบนี้คือ: " พยายามลบเครื่องหมายจุลภาคที่ 11 ข้ามไปบรรทัดถัดไปหากคุณล้มเหลวลองแทนที่เครื่องหมายจุลภาคที่ 10 ด้วยตัวเองพิมพ์บรรทัดถ้าคุณทำสำเร็จ " awkคำตอบให้คำแนะนำกับคอมพิวเตอร์เพียงวิธีการที่คุณจะแสดงให้พวกเขาในภาษาอังกฤษ ( awkดีสำหรับข้อมูลที่อิงกับฟิลด์)
Wildcard

4

โยนสั้น ๆpython:

#!/usr/bin/env python2
with open('file.csv') as f:
    print '\n'.join(line for line in f if line.count(',') == 10)

นี้จะอ่านแต่ละบรรทัดและตรวจสอบว่าจำนวนของเครื่องหมายจุลภาคในบรรทัดเท่ากับ 10 line.count(',') == 10หรือไม่ถ้าเป็นเช่นนั้นพิมพ์จะเป็นบรรทัด


2

และนี่คือวิธี Perl:

perl -F, -ane 'print if $#F==10'

-nทำให้เกิดperlการอ่านบรรทัดแฟ้มใส่ของโดยสายและรันสคริปต์ที่กำหนดโดย-eในแต่ละบรรทัด -aจะเปิดแยกอัตโนมัติ: สายการป้อนข้อมูลแต่ละคนจะถูกแบ่งออกกับมูลค่าที่ได้รับจาก-F(ที่นี่จุลภาค) @Fและบันทึกไว้เป็นอาร์เรย์

$#F(หรือมากกว่าปกติ$#array) @Fเป็นดัชนีที่สูงที่สุดของอาร์เรย์ ตั้งแต่อาร์เรย์เริ่มต้นที่0เส้นกับ 11 สาขาจะมีของ@F 10ดังนั้นสคริปต์จะพิมพ์บรรทัดหากมี 11 ฟิลด์ที่แน่นอน


นอกจากนี้คุณยังสามารถทำเป็นprint if @F==11อาร์เรย์ในบริบทสเกลาร์ส่งคืนจำนวนองค์ประกอบ
Sobrique

1

หากเขตข้อมูลสามารถมีเครื่องหมายจุลภาคหรือขึ้นบรรทัดใหม่รหัสของคุณจำเป็นต้องเข้าใจ csv ตัวอย่าง (มีสามคอลัมน์):

$ cat filter.csv
a,b,c
d,"e,f",g
1,2,3,4
one,two,"three
...continued"

$ cat filter.csv | python3 -c 'import sys, csv
> csv.writer(sys.stdout).writerows(
> row for row in csv.reader(sys.stdin) if len(row) == 3)
> '
a,b,c
d,"e,f",g
one,two,"three
...continued"

ฉันคิดว่าแนวทางแก้ไขส่วนใหญ่จนถึงตอนนี้จะยกเลิกแถวที่สองและสี่

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