Computer >> คอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

เคล็ดลับทับทิม Regex สุดเจ๋ง

ฉันคิดว่าน่าจะสนุกที่จะติดตามบทความของเมื่อวานเกี่ยวกับเงื่อนไข regex โดยดูที่ลูกเล่นอื่นๆ ที่คุณสามารถทำได้ด้วยนิพจน์ทั่วไปใน ruby

การแยกสตริงด้วยนิพจน์ทั่วไป

คุณอาจค่อนข้างคุ้นเคยกับการแยกสตริงโดยใช้ตัวคั่นข้อความ:

"one,two".split(",")
# => ["one", "two"]

แต่คุณรู้หรือไม่ว่าการแยกจะยอมรับนิพจน์ทั่วไปด้วย

# use `,` and `-` as delimiters
"one,two-three".split(/,|-/)
=> ["one", "two", "three"]

# Use comma as thousands separator for US currency,
# but use a space for Euros
"1,000USD".split /(?=.*(USD))(?(1),| )/

จับตัวคั่น

นี่คือเคล็ดลับปาร์ตี้ที่เรียบร้อย โดยปกติ เมื่อคุณแยกสตริง ตัวคั่นจะหายไป:

# The commas vanish!
"one,two".split(",")
# => ["one", "two"]

แต่ถ้าคุณใช้นิพจน์ทั่วไปและคุณใส่ตัวคั่นในกลุ่ม split จะจับตัวคั่นด้วย

"one,two-three".split(/(,|-)/)
=> ["one", ",", "two", "-", "three"]

สาเหตุที่เกิดขึ้นก็คือ split จริง ๆ แล้วแยกสตริงที่ขอบเขตของกลุ่มดักจับแต่ละกลุ่ม

ละเมิด split

คุณสามารถใช้ split . ในทางที่ผิด เพื่อให้มีลักษณะเหมือน match . ในโค้ดด้านล่าง ฉันใช้สี่กลุ่มในนิพจน์ทั่วไปเพื่อแยกสตริงออกเป็น 4 ส่วน

"1-800-555-1212".split(/(1)-(\d{3})-(\d{3})-(\d{4})/)
=> ["", "1", "800", "555", "1212"]

รายการแรกในอาร์เรย์ผลลัพธ์เป็นสตริงว่างเนื่องจากนิพจน์ทั่วไปตรงกับสตริงต้นทางทั้งหมด

การจับคู่ทั่วโลก

โดยค่าเริ่มต้น นิพจน์ทั่วไปจะจับคู่รูปแบบเพียงครั้งเดียว ในโค้ดด้านล่าง เราได้รับเพียงหนึ่งรายการที่ตรงกัน แม้ว่าจะมีการจับคู่ที่เป็นไปได้ห้ารายการ

"12345".match /\d/
=> #<MatchData "1">

ในภาษาอื่นๆ เช่น Perl วิธีแก้ไขคือตั้งค่าสถานะนิพจน์ทั่วไปเป็น "ทั่วโลก" Ruby ไม่มีตัวเลือกนั้น แต่มี String#scan กระบวนการ.

scan method ส่งคืนอาร์เรย์ที่มีการแข่งขันทั้งหมด:

"12345".scan /\d/
=> ["1", "2", "3", "4", "5"]

มันยังมีรูปแบบการบล็อกที่สะดวก:

"12345".scan /\d/ do |i|
  puts i
end

น่าเสียดายที่ดูเหมือนว่าจะไม่มีวิธีใดในการสแกนสตริงอย่างเกียจคร้าน ดังนั้นเทคนิคนี้อาจไม่เหมาะสำหรับ - พูด - การประมวลผลไฟล์ 500mb

การสแกนด้วยกลุ่ม

ณ จุดนี้ ฉันหวังว่าคุณจะสงสัยว่าเราสามารถใช้กลอุบายแปลก ๆ แบบใดได้บ้างโดยใช้กลุ่มในการสแกนของเรา

น่าเสียดายที่พฤติกรรมที่นี่คาดเดาได้อย่างสมบูรณ์และน่าเบื่อ กลุ่มส่งผลให้อาร์เรย์หลายมิติ:

"hiho hiho".scan /(hi)(ho)/
=> [["hi", "ho"], ["hi", "ho"]]

มีเคสขอบแปลกอยู่หนึ่งเคส หากคุณใช้กลุ่ม สิ่งใดที่ไม่อยู่ในกลุ่มจะไม่ถูกส่งกลับ

"hiho hiho".scan /(hi)ho/
=> [["hi"], ["hi"]]

ชอร์ตแฮนด์

ฉันพนันว่าคุณรู้เกี่ยวกับ =~ เพื่อตรวจสอบว่านิพจน์ทั่วไปตรงกับสตริงหรือไม่ ส่งกลับดัชนีของตัวละครที่การแข่งขันเริ่มต้น

"hiho" =~ /hi/
# 0
"hiho" =~ /ho/
# 2

มีอีกวิธีที่รวดเร็วในการตรวจสอบการจับคู่ ฉันกำลังพูดถึง === โอเปอเรเตอร์

/hi/ === "hiho"
# true

เมื่อเราเขียน a === b ในทับทิมเรากำลังถามว่า "b อยู่ในชุดที่กำหนดโดย a" หรือไม่ หรือในกรณีนี้ "hiho" อยู่ในชุดของสตริงที่จับคู่โดย regex /hi/ ".

=== ตัวดำเนินการถูกใช้ภายในในกรณีของ Ruby นั่นหมายความว่า คุณสามารถใช้นิพจน์ทั่วไปในคำสั่ง case ได้เช่นกัน

case "hiho"
when /hi/
  puts "match"
end

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

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

def ignored?(error)
  @ignored_patterns.any? { |x| x === error.class.name }
end

แค่นั้น!

เพื่อให้แน่ใจว่ามีเทคนิคเล็กๆ น้อยๆ เช่นนี้กระจายอยู่ทั่ว Ruby and Rails หากมีสิ่งใดที่คุณชอบเป็นพิเศษ แจ้งให้เราทราบ!