นิพจน์ทั่วไปของ Ruby (ruby regex สั้นๆ) ช่วยให้คุณค้นหารูปแบบเฉพาะภายในสตริง โดยมีเจตนาที่จะดึงข้อมูลเพื่อการประมวลผลต่อไป
กรณีการใช้งานทั่วไป 2 กรณีสำหรับนิพจน์ทั่วไป ได้แก่ การตรวจสอบความถูกต้องและการแยกวิเคราะห์
ตัวอย่างเช่น :
นึกถึงที่อยู่อีเมลด้วย ruby regex คุณสามารถกำหนดว่าที่อยู่อีเมลที่ถูกต้องเป็นอย่างไร กล่าวอีกนัยหนึ่ง โปรแกรมของคุณจะสามารถบอกความแตกต่างระหว่างที่อยู่อีเมลที่ถูกต้องและไม่ถูกต้อง
นิพจน์ทั่วไปของ Ruby ถูกกำหนดระหว่างเครื่องหมายทับสองอัน เพื่อแยกความแตกต่างจากไวยากรณ์ภาษาอื่น นิพจน์ที่ง่ายที่สุดจะจับคู่คำหรือตัวอักษรเดียว
ตัวอย่างเช่น :
# Find the word 'like' "Do you like cats?" =~ /like/
ส่งคืนดัชนีของการเกิดขึ้นครั้งแรกของคำหากพบ (จับคู่สำเร็จ) หรือ nil
มิฉะนั้น. ถ้าเราไม่สนใจดัชนี เราสามารถใช้ String#include ได้หรือไม่ วิธีการ
อีกวิธีในการตรวจสอบว่าสตริงตรงกับ regex หรือไม่คือการใช้ match
วิธีการ:
if "Do you like cats?".match(/like/) puts "Match found!" end
ตอนนี้:
คุณจะได้เรียนรู้วิธีสร้างรูปแบบขั้นสูงขึ้นเพื่อให้จับคู่ บันทึก และแทนที่สิ่งต่างๆ เช่น วันที่ หมายเลขโทรศัพท์ อีเมล URL ฯลฯ
คลาสตัวละคร
คลาสอักขระช่วยให้คุณกำหนดช่วงหรือรายการอักขระที่จะจับคู่ได้ ตัวอย่างเช่น [aeiou]
ตรงกับสระใด ๆ
ตัวอย่าง :สตริง มี . หรือไม่ สระ?
def contains_vowel(str) str =~ /[aeiou]/ end contains_vowel("test") # returns 1 contains_vowel("sky") # returns nil
นี้จะไม่คำนึงถึง จำนวนเงิน ของตัวละครเราจะได้เห็นวิธีการทำในเร็ว ๆ นี้
ช่วง
เราสามารถใช้ช่วงเพื่อจับคู่ตัวอักษรหรือตัวเลขหลายตัวโดยไม่ต้องพิมพ์ทั้งหมด กล่าวคือ ช่วงเช่น [2-5]
เหมือนกับ [2345]
.
ช่วงที่มีประโยชน์บางส่วน:
- [0-9] จับคู่ตัวเลขใดก็ได้ตั้งแต่ 0 ถึง 9
- [a-z] จับคู่ตัวอักษรใดก็ได้ตั้งแต่ a ถึง z (ไม่มีตัวพิมพ์ใหญ่)
- [^a-z] ช่วงลบ
ตัวอย่าง :สตริงนี้มีตัวเลขหรือไม่
def contains_number(str) str =~ /[0-9]/ end contains_number("The year is 2015") # returns 12 contains_number("The cat is black") # returns nil
ข้อควรจำ:ค่าที่ส่งคืนเมื่อใช้ `=~` อาจเป็นดัชนีสตริงหรือ `nil`
มีไวยากรณ์ชวเลขที่ดีสำหรับการระบุช่วงอักขระ:
- \w เทียบเท่ากับ [0-9a-zA-Z_]
- \d เหมือนกับ [0-9]
- \s ตรงกับ ช่องว่าง (แท็บ พื้นที่ปกติ ขึ้นบรรทัดใหม่)
นอกจากนี้ยังมีรูปแบบเชิงลบเหล่านี้:
- \W อะไรก็ได้ที่ไม่ใช่ [0-9a-zA-Z_]
- \D อะไรก็ได้ที่ไม่ใช่ตัวเลข
- \S อะไรก็ได้ที่ไม่ใช่ช่องว่าง
อักขระจุด .
ตรงกับทุกอย่างยกเว้นบรรทัดใหม่ หากคุณต้องการใช้ตัวอักษร .
แล้วคุณจะต้องหลบหนีมัน
ตัวอย่าง :หนีอักขระพิเศษ
# If we don't escape, the letter will match "5a5".match(/\d.\d/) # In this case only the literal dot matches "5a5".match(/\d\.\d/) # nil "5.5".match(/\d\.\d/) # match
ตัวแก้ไข
จนถึงตอนนี้เราสามารถจับคู่ตัวละครได้ทีละตัวเท่านั้น เพื่อให้ตรงกับอักขระหลายตัว เราสามารถใช้ตัวปรับเปลี่ยนรูปแบบได้
ตัวแก้ไข | คำอธิบาย |
---|---|
+ | 1 หรือมากกว่า |
* | 0 ขึ้นไป |
? | 0 หรือ 1 |
{3,5} | ระหว่าง 3 ถึง 5 |
เราสามารถรวมทุกสิ่งที่เราเรียนรู้มาเพื่อสร้างนิพจน์ทั่วไปที่ซับซ้อนยิ่งขึ้นได้
ตัวอย่าง :ดูเหมือนที่อยู่ IP หรือไม่
# Note that this will also match some invalid IP address # like 999.999.999.999, but in this case we just care about the format. def ip_address?(str) # We use !! to convert the return value to a boolean !!(str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/) end ip_address?("192.168.1.1") # returns true ip_address?("0000.0000") # returns false
การจับคู่สตริงที่แน่นอน
หากคุณต้องการการจับคู่แบบตรงทั้งหมด คุณจะต้องมีตัวแก้ไขประเภทอื่น มาดูตัวอย่างกันเพื่อดูว่าฉันกำลังพูดถึงอะไร:
# We want to find if this string is exactly four letters long, this will # still match because it has more than four, but it's not what we want. "Regex are cool".match /\w{4}/ # Instead we will use the 'beginning of line' and 'end of line' modifiers "Regex are cool".match /^\w{4}$/ # This time it won't match. This is a rather contrived example, since we could just # have used .size to find the length, but I think it gets the idea across.
เพื่อจับคู่ที่จุดเริ่มต้นของสตริงอย่างเคร่งครัดและไม่ใช่แค่ทุกบรรทัด (หลัง \n
) คุณต้องใช้ \A
และ \Z
แทน ^
และ $
.
จับกลุ่ม
ด้วยแคปเจอร์กรุ๊ป เราสามารถจับภาพบางส่วนของการแข่งขันและนำกลับมาใช้ใหม่ได้ในภายหลัง ในการจับภาพการแข่งขัน เราใส่ส่วนที่เราต้องการจับไว้ในวงเล็บ
ตัวอย่าง :การแยกวิเคราะห์ไฟล์บันทึก
Line = Struct.new(:time, :type, :msg) LOG_FORMAT = /(\d{2}:\d{2}) (\w+) (.*)/ def parse_line(line) line.match(LOG_FORMAT) { |m| Line.new(*m.captures) } end parse_line("12:41 INFO User has logged in.") # This produces objects like this: #
ในตัวอย่างนี้ เรากำลังใช้ .match
แทน =~
.
เมธอดนี้ส่งคืน MatchData
วัตถุหากมีการแข่งขันไม่มีมิฉะนั้น MatchData
class มีวิธีการที่มีประโยชน์มากมาย โปรดดูเอกสารเพื่อเรียนรู้เพิ่มเติม!
หากคุณต้องการเพียงค่าบูลีน (true
/ false
) จากนั้นคุณสามารถใช้ match?
ซึ่งสามารถใช้ได้ตั้งแต่ Ruby 2.4 นอกจากนี้ยังเร็วกว่า match
เนื่องจาก Ruby ไม่จำเป็นต้องสร้าง MatchData
วัตถุ
คุณสามารถเข้าถึงข้อมูลที่บันทึกไว้โดยใช้ .captures
วิธีการหรือการรักษา MatchData
วัตถุเช่นอาร์เรย์ ดัชนีศูนย์จะมีการจับคู่แบบเต็ม และดัชนีที่ตามมาจะมีกลุ่มที่ตรงกัน
หากคุณต้องการแคปเจอร์กรุ๊ปแรก คุณสามารถทำได้:
m = "John 31".match /\w+ (\d+)/ m[1] # 31
คุณยังสามารถมีกลุ่มที่ไม่ได้จับ พวกเขาจะให้คุณจัดกลุ่มนิพจน์เข้าด้วยกันโดยไม่มีการปรับประสิทธิภาพ คุณอาจพบว่ากลุ่มที่มีชื่อมีประโยชน์ในการทำให้นิพจน์ที่ซับซ้อนอ่านง่ายขึ้น
ไวยากรณ์ | คำอธิบาย |
---|---|
(?:...) | ไม่จับกลุ่ม |
(?<foo>...) | ตั้งชื่อกลุ่ม |
ตัวอย่าง :กลุ่มที่มีชื่อ
m = "David 30".match /(?\w+) (?\d+)/ m[:age] # => "30" m[:name] # => "David"
กลุ่มที่มีชื่อส่งคืน MatchData
วัตถุที่คุณสามารถเข้าไปอ่านผลได้
มองไปข้างหน้าและมองข้างหลัง
นี่เป็นเทคนิคขั้นสูงที่อาจไม่มีในการใช้งาน regex ทั้งหมด เครื่องมือนิพจน์ทั่วไปของ Ruby สามารถทำได้ ดังนั้นเรามาดูวิธีใช้ประโยชน์จากสิ่งนั้นกัน
มองไปข้างหน้าให้เราแอบดูและดูว่ามีการจับคู่เฉพาะก่อนหรือหลังหรือไม่
ชื่อ | คำอธิบาย |
---|---|
(?=pat) | มองไปข้างหน้าในเชิงบวก |
(?<=pat) | มองข้างหลังเป็นบวก |
(?!pat) | มองไปข้างหน้าเชิงลบ |
(? | มองข้างหลังเชิงลบ |
ตัวอย่าง :มีตัวเลขนำหน้าด้วยตัวอักษรอย่างน้อยหนึ่งตัวหรือไม่
def number_after_word?(str) !!(str =~ /(?<=\w) (\d+)/) end number_after_word?("Grade 99")
คลาส Ruby's Regex
นิพจน์ทั่วไปของ Ruby เป็นอินสแตนซ์ของ Regexp
ระดับ. ส่วนใหญ่คุณจะไม่ใช้คลาสนี้โดยตรง แต่เป็นการดีที่จะรู้ 🙂
puts /a/.class # Regexp
การใช้งานที่เป็นไปได้อย่างหนึ่งคือสร้าง regex จากสตริง:
regexp = Regexp.new("a")
อีกวิธีหนึ่งในการสร้าง regexp:
regexp = %r{\w+}
ตัวเลือก Regex
คุณสามารถตั้งค่าตัวเลือกบางอย่างในนิพจน์ทั่วไปเพื่อทำให้นิพจน์ทั่วไปทำงานแตกต่างออกไปได้
ตัวเลือก | คำอธิบาย |
---|---|
ผม | ruby regex ไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่ |
ม | จุดตรงขึ้นบรรทัดใหม่ |
x | ละเว้นช่องว่าง |
หากต้องการใช้ตัวเลือกเหล่านี้ คุณต้องเพิ่มตัวอักษรที่ส่วนท้ายของ regex หลังปิด /
.
ถูกใจสิ่งนี้ :
"abc".match?(/[A-Z]/i)
การจัดรูปแบบนิพจน์ทั่วไปแบบยาว
นิพจน์ทั่วไปที่ซับซ้อนของ Ruby อาจอ่านได้ยาก ดังนั้นจะเป็นประโยชน์หากเราแบ่งมันออกเป็นหลายบรรทัด เราสามารถทำได้โดยใช้ตัวปรับแต่ง 'x' รูปแบบนี้ยังช่วยให้คุณใช้ความคิดเห็นภายใน regex ของคุณได้อีกด้วย
ตัวอย่าง :
LOG_FORMAT = %r{ (\d{2}:\d{2}) # Time \s(\w+) # Event type \s(.*) # Message }x
Ruby regex:รวมทุกอย่างเข้าด้วยกัน
นิพจน์ทั่วไปสามารถใช้ได้กับวิธี Ruby มากมาย
- .split
- .scan
- .gsub
- และอีกมากมาย…
ตัวอย่าง :จับคู่คำทั้งหมดจากสตริงโดยใช้ .scan
"this is some string".scan(/\w+/) # => ["this", "is", "some", "string"]
ตัวอย่าง :แยกตัวเลขทั้งหมดออกจากสตริง
"The year was 1492.".scan(/\d+/) # => ["1492"]
ตัวอย่าง :ใช้อักษรตัวพิมพ์ใหญ่ทุกคำในสตริง
str = "lord of the rings" str.gsub(/\w+/) { |w| w.capitalize } # => "Lord Of The Rings"
ตัวอย่าง :ตรวจสอบที่อยู่อีเมล
email = "[email protected]" !!email.match(/\A[\w.+-]+@\w+\.\w+\z/) # true
ตัวอย่างสุดท้ายนี้ใช้ !!
เพื่อแปลงผลลัพธ์เป็นค่าบูลีน (true
/ false
) หรือคุณสามารถใช้ match?
วิธีใน Ruby 2.4+ ซึ่งทำสิ่งนี้ให้คุณแล้ว &เร็วกว่าเช่นกัน
บทสรุป
นิพจน์ทั่วไปนั้นน่าทึ่ง แต่บางครั้งอาจดูยุ่งยากเล็กน้อย การใช้เครื่องมืออย่าง rubular.com จะช่วยคุณสร้าง ruby regex ในลักษณะโต้ตอบมากขึ้น Rubular ยังมีแผ่นโกงนิพจน์ทั่วไปของ Ruby ที่คุณจะพบว่ามีประโยชน์มาก ตอนนี้ถึงตาคุณแล้วที่จะเปิดโปรแกรมแก้ไขและเริ่มเขียนโค้ด!
โอ้ และอย่าลืม แชร์สิ่งนี้ กับเพื่อน ๆ ของคุณถ้าคุณสนุกกับมันเพื่อให้คนอื่นสามารถเรียนรู้ได้ 🙂