ค้นหาข้อความระหว่างอักขระหรือสตริงที่ระบุสองตัว


17

พูดว่าฉันมีสายเช่นนี้:

*[234]*
*[23]*
*[1453]*

โดยที่*แสดงถึงสตริงใด ๆ (ยกเว้นสตริงของฟอร์ม[number]) ฉันจะแยกวิเคราะห์บรรทัดเหล่านี้ด้วยยูทิลิตีบรรทัดคำสั่งและแยกตัวเลขระหว่างวงเล็บได้อย่างไร

มากกว่าปกติซึ่งเครื่องมือเหล่านี้cut, sed, grepหรือawkจะเป็นที่เหมาะสมสำหรับงานดังกล่าวหรือไม่

คำตอบ:


16

หากคุณมี GNU grep คุณสามารถใช้มันได้ -oตัวเลือกเพื่อค้นหา regex และส่งออกเฉพาะส่วนที่จับคู่ (การใช้งาน grep อื่น ๆ สามารถแสดงทั้งบรรทัดได้) หากมีการแข่งขันหลายรายการในหนึ่งบรรทัด

grep -o '\[[0-9]*\]'

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

grep -P -o '(?<=\[)[0-9]*(?=\])'

ด้วย sed คุณจะต้องปิดการพิมพ์ด้วย-nและจับคู่ทั้งบรรทัดและเก็บเฉพาะส่วนที่ตรงกัน หากมีการแข่งขันที่เป็นไปได้หลายรายการในหนึ่งบรรทัดจะมีการพิมพ์เฉพาะนัดสุดท้ายเท่านั้น ดูที่การแยก regex ที่ตรงกับ 'sed' โดยไม่ต้องพิมพ์อักขระโดยรอบสำหรับรายละเอียดเพิ่มเติมเกี่ยวกับการใช้ sed ที่นี่

sed -n 's/^.*\(\[[0-9]*\]\).*/\1/p'

หรือถ้าคุณต้องการตัวเลขและไม่ใช่วงเล็บ:

sed -n 's/^.*\[\([0-9]*\)\].*/\1/p'

หากไม่มีgrep -oPerl ก็เป็นเครื่องมือที่ดีที่สุดหากคุณต้องการบางสิ่งที่เรียบง่ายและเข้าใจง่าย ในทุกบรรทัด ( -n) หากบรรทัดมีการจับคู่สำหรับ\[[0-9]*\]ให้พิมพ์การจับคู่นั้น ( $&) และการขึ้นบรรทัดใหม่ ( -l)

perl -l -ne '/\[[0-9]*\]/ and print $&'

หากคุณต้องการเฉพาะตัวเลขให้ใส่เครื่องหมายวงเล็บใน regex เพื่อกำหนดกลุ่มและพิมพ์เฉพาะกลุ่มนั้น

perl -l -ne '/\[([0-9]*)\]/ and print $1'

ป.ล. ถ้าคุณต้องการเพียงหนึ่งหรือมากกว่าหลักระหว่างวงเล็บเปลี่ยน[0-9]*เป็น[0-9][0-9]*หรือ[0-9]+ใน Perl


ทุกอย่างดีนอกจากนั้นเขาต้องการ "แยกจำนวนระหว่างวงเล็บ" ฉันคิดว่า "ยกเว้น[number]" หมายถึงยกเว้น[0-9]
Peter.O

1
@ Peter.OI เข้าใจ "ยกเว้น [number]" เพื่อหมายความว่าไม่มีส่วนอื่น ๆ ของบรรทัดของแบบฟอร์มนั้น แต่ฉันได้แก้ไขคำตอบของฉันเพื่อแสดงวิธีพิมพ์เฉพาะตัวเลขในกรณี
Gilles 'ดังนั้น - หยุดความชั่วร้าย'

1
การperlยืนยัน regex เหล่านั้นดูมีประโยชน์จริงๆ! ฉันได้อ่านเกี่ยวกับพวกเขาหลังจากเห็นว่าคุณใช้การยืนยันย้อนหลังและไปข้างหน้าแม้จะเป็น grep (ฉันจะปิดการใช้งานจริงที่คุณสามารถเลือกเครื่องมือ regex) ฉันจะอุทิศเวลาอีกเล็กน้อยเพื่อ regex ของ perl จากที่นี่ใน ขอบคุณ ... ป.ล. ฉันเพิ่งอ่านมาman grep... "นี่เป็นการทดลองขั้นสูงและ grep -P อาจเตือนถึงคุณสมบัติที่ยังไม่ได้ใช้งาน" ... ฉันหวังว่าจะไม่ได้หมายความว่าไม่เสถียร (?) ...
Peter.O

5

cutคุณไม่สามารถทำมันได้ด้วย

  1. tr -c -d '0123456789\012'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

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


สำหรับ sed, ^.*โลภและสิ้นเปลืองทั้งหมด แต่เป็นเลขหลักสุดท้ายและ+จำเป็นต้องมี\+หรือใช้ posix \([0-9][0-9]*\).... และในกรณีใด ๆ's/[^0-9]*//g'ก็ใช้งานได้เช่นกัน... Thanks for the ตัวอย่าง tr-c` แต่ไม่ได้\012แฝงความลับเลยหรือ
Peter.O

@ Peter ขอบคุณสำหรับการจับที่ ฉันสาบานได้แล้วว่าฉันได้ทดสอบตัวอย่าง :( ฉันเปลี่ยนมันเป็นเวอร์ชั่นของคุณเกี่ยวกับ\012: มันเป็นสิ่งจำเป็นมิฉะนั้นtrจะกินบรรทัดใหม่
Kyle Jones

อ้า ... ผมก็เห็นว่ามันเป็น\0, 1, 2(หรือแม้กระทั่ง \, 0, 1, 2) ฉันไม่สนิทพอที่จะแปดดูเหมือนว่า .. ขอบคุณ
Peter.O

4

หากคุณหมายถึงการแยกชุดของตัวเลขที่ต่อเนื่องระหว่างตัวละครที่ไม่ใช่ตัวเลขฉันเดาsedและawkดีที่สุด (แม้ว่าgrepจะสามารถให้อักขระที่ตรงกับคุณ):

sed: แน่นอนว่าคุณสามารถจับคู่ตัวเลขได้ แต่อาจน่าสนใจที่จะทำตรงกันข้ามให้ลบตัวเลขที่ไม่ใช่ตัวเลขออก (ทำงานเท่าที่มีเพียงหนึ่งหมายเลขต่อบรรทัด):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep: คุณสามารถจับคู่ตัวเลขที่ต่อเนื่องกัน

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

ฉันไม่ได้ยกตัวอย่างawkเพราะฉันมีประสบการณ์ที่ไร้ค่า; เป็นที่น่าสนใจที่จะทราบว่าแม้ว่าsedจะเป็นมีดแบบสวิสgrepให้วิธีที่ง่ายและอ่านง่ายกว่าในการทำเช่นนี้ซึ่งทำงานได้มากกว่าหนึ่งหมายเลขในแต่ละบรรทัดอินพุต ( -oพิมพ์เฉพาะส่วนที่ตรงกันของอินพุตแต่ละอัน ในบรรทัดของตัวเอง):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54

เช่นเดียวกับการเปรียบเทียบที่นี่เป็นsedeqivalent ของ"มากกว่าจำนวนหนึ่งต่อบรรทัด"grep -o '[[:digit:]]*'ตัวอย่างเช่น . . sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Peter.O

2

เนื่องจากได้มีการกล่าวว่าสิ่งนี้ไม่สามารถทำได้cutฉันจะแสดงให้เห็นว่าเป็นไปได้อย่างง่ายดายที่จะสร้างวิธีแก้ปัญหาที่อย่างน้อยก็ไม่ได้เลวร้ายไปกว่าคนอื่น ๆ แม้ว่าฉันจะไม่รับรองการใช้ในcutฐานะ "ดีที่สุด" (หรือแม้แต่โซลูชันที่ดีเป็นพิเศษ) มันควรจะกล่าวว่าวิธีการแก้ปัญหาใด ๆ ที่ไม่ได้มองหาโดยเฉพาะ*[และ]*รอบ ๆ ตัวเลขทำให้สมมติฐานง่ายขึ้นและดังนั้นจึงมีแนวโน้มที่จะล้มเหลวในตัวอย่างที่ซับซ้อนกว่านั้นอีกหนึ่งที่ได้รับจากผู้ถาม (เช่นตัวเลขภายนอก*[และ]*ซึ่งไม่ควรแสดง) วิธีการแก้ปัญหานี้จะตรวจสอบอย่างน้อยที่สุดสำหรับวงเล็บและสามารถขยายเพื่อตรวจสอบเครื่องหมายดอกจันได้เช่นกัน

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

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

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