วิธีจับคู่ regex ทั้งหมดที่เกิดขึ้น


586

มีวิธีที่รวดเร็วในการค้นหาการจับคู่ของนิพจน์ทั่วไปใน Ruby หรือไม่? ฉันได้ตรวจสอบวัตถุ Regex ใน Ruby STL และค้นหาใน Google โดยไม่มีประโยชน์


3
ผมอ่านนี้เป็นวิธีที่ฉันสามารถค้นหาสตริงสำหรับรูปแบบ regex ทั้งหมดและสับสนอย่างน่ากลัว ...
Hugoagogo

คำตอบ:


821

การใช้scanควรทำเคล็ดลับ:

string.scan(/regex/)

9
แต่กรณีนี้คืออะไร "จับคู่ฉัน!". scan (/.../) = ["mat", "ch" "me!" ] แต่สิ่งที่เกิดขึ้นทั้งหมดของ /.../ จะเป็น ["mat", "atc", "tch", "ch", ... ]
Michael Dickens

13
ไม่มันจะไม่เป็น /.../ เป็น regexp โลภปกติ มันจะไม่ย้อนรอยในเนื้อหาที่ตรงกัน คุณสามารถลองใช้ regexp แบบขี้เกียจ แต่ก็อาจจะไม่เพียงพอ มีลักษณะที่ regexp เอกสารruby-doc.org/core-1.9.3/Regexp.htmlอย่างถูกต้องแสดง regexp ของคุณ :)
ฌอง

49
ดูเหมือนว่า Ruby WTF ... ทำไมจึงเป็นสิ่งนี้ใน String แทนที่จะเป็น Regexp กับสิ่งอื่น ๆ ของ regexp ไม่ได้กล่าวถึงแม้แต่ในเอกสารสำหรับ Regexp
Anentropic

9
ฉันเดาว่าเป็นเพราะมีการกำหนดและเรียกใช้ String ไม่ใช่บน Regex ... แต่จริงๆแล้วมันสมเหตุสมผลแล้ว คุณสามารถเขียนนิพจน์ทั่วไปเพื่อจับคู่การแข่งขันทั้งหมดโดยใช้ Regex # match และวนซ้ำในกลุ่มที่จับได้ ที่นี่คุณเขียนฟังก์ชั่นการจับคู่บางส่วนและต้องการให้มันใช้หลายครั้งในสตริงที่กำหนดนี่ไม่ใช่ความรับผิดชอบของ Regexp ฉันขอแนะนำให้คุณตรวจสอบการใช้งานสแกนเพื่อความเข้าใจที่ดีขึ้น: ruby-doc.org/core-1.9.3/String.html#method-i-scan
Jean

9
@MichaelDickens: /(?=(...))/ในกรณีนี้คุณสามารถใช้
Konrad Borowski

67

ในการค้นหาสตริงที่ตรงกันทั้งหมดให้ใช้scanวิธีการของสตริง

str = "A 54mpl3 string w1th 7 numb3rs scatter36 ar0und"
str.scan(/\d+/)
#=> ["54", "3", "1", "7", "3", "36", "0"]

หากคุณต้องการMatchDataซึ่งเป็นประเภทของวัตถุที่ส่งคืนโดยmatchวิธีRegexp ให้ใช้:

str.to_enum(:scan, /\d+/).map { Regexp.last_match }
#=> [#<MatchData "54">, #<MatchData "3">, #<MatchData "1">, #<MatchData "7">, #<MatchData "3">, #<MatchData "36">, #<MatchData "0">]

ประโยชน์ของการใช้MatchDataคือคุณสามารถใช้วิธีการเช่นoffset:

match_datas = str.to_enum(:scan, /\d+/).map { Regexp.last_match }
match_datas[0].offset(0)
#=> [2, 4]
match_datas[1].offset(0)
#=> [7, 8]

ดูคำถามเหล่านี้หากคุณต้องการทราบข้อมูลเพิ่มเติม:

อ่านเกี่ยวกับตัวแปรพิเศษ$&, $', $1, $2ทับทิมจะเป็นประโยชน์มากเกินไป


12

หากคุณมี regexp กับกลุ่ม:

str="A 54mpl3 string w1th 7 numbers scatter3r ar0und"
re=/(\d+)[m-t]/

คุณสามารถใช้scanวิธีการของ String เพื่อค้นหากลุ่มที่ตรงกัน:

str.scan re
#> [["54"], ["1"], ["3"]]

วิธีค้นหารูปแบบการจับคู่:

str.to_enum(:scan,re).map {$&}
#> ["54m", "1t", "3r"]

str.scan(/\d+[m-t]/) # => ["54m", "1t", "3r"]มีสำนวนมากกว่าstr.to_enum(:scan,re).map {$&}
Tin Man

บางทีคุณอาจเข้าใจผิด การแสดงออกปกติของตัวอย่างของผู้ใช้ที่ฉันตอบคือ: /(\d+)[m-t]/ไม่/\d+[m-t]/เขียน: re = /(\d+)[m-t]/; str.scan(re)เหมือนกันstr.scan(/(\d+)[mt]/)แต่ฉันได้ #> [["" 54 "], [" 1 "], [" 3 "]]ไม่ใช่"54m", "1t", "3r"]คำถามคือถ้าฉันมีการแสดงออกปกติกับกลุ่มและต้องการจับรูปแบบทั้งหมดโดยไม่ต้องเปลี่ยนปกติ การแสดงออก (ออกจากกลุ่ม) ฉันจะทำอย่างไร ในแง่นี้คำตอบที่เป็นไปได้แม้ว่าจะเป็นความลับเล็ก ๆ น้อย ๆ และอ่านยากก็คือ:str.to_enum(:scan,re).map {$&}
MVP

-1

string.scan(your_regex).flattenคุณสามารถใช้ หาก regex ของคุณมีกลุ่มมันจะกลับมาในอาร์เรย์ธรรมดาเดียว

string = "A 54mpl3 string w1th 7 numbers scatter3r ar0und"
your_regex = /(\d+)[m-t]/
string.scan(your_regex).flatten
=> ["54", "1", "3"]

Regex สามารถเป็นกลุ่มที่มีชื่อเช่นกัน

string = 'group_photo.jpg'
regex = /\A(?<name>.*)\.(?<ext>.*)\z/
string.scan(regex).flatten

คุณสามารถใช้gsubมันเป็นอีกวิธีหนึ่งถ้าคุณต้องการ MatchData

str.gsub(/\d/).map{ Regexp.last_match }

นำการจัดกลุ่มจากและคุณจะไม่จำเป็นต้องใช้your_regex = /(\d+)[m-t]/ flattenใช้ตัวอย่างสุดท้ายของคุณlast_matchซึ่งในกรณีนี้น่าจะเป็นความปลอดภัย แต่เป็นทั่วโลกและอาจจะถูกเขียนทับถ้า regex ใด ๆ last_matchที่ถูกจับคู่ก่อนที่จะโทร แต่อาจจะปลอดภัยกว่าที่จะใช้string.match(regex).captures # => ["group_photo", "jpg"]หรือstring.scan(/\d+/) # => ["54", "3", "1", "7", "3", "0"]ดังที่แสดงในคำตอบอื่น ๆ ขึ้นอยู่กับรูปแบบและความต้องการ
ชายดีบุก
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.