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

ฝึกฝนนิพจน์ทั่วไปของ Ruby

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

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

ตัวอย่างเช่น :

นึกถึงที่อยู่อีเมลด้วย ruby regex คุณสามารถกำหนดว่าที่อยู่อีเมลที่ถูกต้องเป็นอย่างไร กล่าวอีกนัยหนึ่ง โปรแกรมของคุณจะสามารถบอกความแตกต่างระหว่างที่อยู่อีเมลที่ถูกต้องและไม่ถูกต้อง

ฝึกฝนนิพจน์ทั่วไปของ Ruby

นิพจน์ทั่วไปของ 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 ที่คุณจะพบว่ามีประโยชน์มาก ตอนนี้ถึงตาคุณแล้วที่จะเปิดโปรแกรมแก้ไขและเริ่มเขียนโค้ด!

โอ้ และอย่าลืม แชร์สิ่งนี้ กับเพื่อน ๆ ของคุณถ้าคุณสนุกกับมันเพื่อให้คนอื่นสามารถเรียนรู้ได้ 🙂