Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm Chủ Biểu Thức Chính Quy Ruby: Khớp Mẫu Nâng Cao & Xác Thực

Biểu thức chính quy Ruby (ruby regex cho ngắn) giúp tìm pattern cụ thể trong chuỗi, nhằm trích xuất dữ liệu xử lý tiếp.

Hai trường hợp dùng phổ biến regex: validation (xác thực) & parsing (phân tích).

Ví dụ:

Nghĩ về email, với ruby regex định nghĩa email hợp lệ trông như thế nào. Nói cách khác, chương trình phân biệt email hợp lệ & không hợp lệ.

Làm Chủ Biểu Thức Chính Quy Ruby: Khớp Mẫu Nâng Cao & Xác Thực

Ruby regular expressions định nghĩa giữa hai dấu gạch chéo phân biệt với cú pháp ngôn ngữ khác. Biểu thức đơn giản khớp từ hoặc một ký tự.

Ví dụ:

# Tìm từ 'like'
"Do you like cats?" =~ /like/

Trả về chỉ số lần xuất hiện đầu nếu tìm thấy (khớp thành công) hoặc nil ngược lại. Nếu không quan tâm chỉ số dùng String#include?.

Cách khác kiểm tra chuỗi khớp regex dùng phương thức match:

if "Do you like cats?".match(/like/)
  puts "Match found!"
end

Bây giờ:

Bạn học xây dựng pattern nâng cao để khớp, bắt获 (capture) & thay thế (replace) thứ như ngày, số điện thoại, email, URL, v.v.

Character Classes (Lớp Ký Tự)

Character class cho định nghĩa dải hoặc danh sách ký tự khớp. Ví dụ, [aeiou] khớp bất kỳ nguyên âm.

Ví dụ: Chuỗi chứa nguyên âm?

def contains_vowel(str)
  str =~ /[aeiou]/
end
contains_vowel("test") # trả về 1
contains_vowel("sky")  # trả về nil

Không tính số lượng ký tự, sẽ thấy cách làm sau.

Ranges (Dải)

Dùng ranges khớp nhiều chữ/số không gõ hết. Nói cách khác, range [2-5] giống [2345].

Một số range hữu ích:

  • [0-9] khớp số 0-9
  • [a-z] khớp chữ a-z (không in hoa)
  • [^a-z] range phủ định

Ví dụ: Chuỗi này chứa số?

def contains_number(str)
  str =~ /[0-9]/
end
contains_number("The year is 2015") # trả về 12
contains_number("The cat is black") # trả về nil

Nhớ: giá trị trả về dùng `=~` là chỉ số chuỗi hoặc `nil`

Có cú pháp viết tắt cho dải ký tự:

  • \w tương đương [0-9a-zA-Z_]
  • \d giống [0-9]
  • \s khớp white space (tab, space, newline)

Cũng có dạng phủ định:

  • \W bất kỳ không trong [0-9a-zA-Z_]
  • \D không phải số
  • \S không phải space

Ký tự chấm . khớp mọi thứ trừ newline. Cần dùng literal . thì escape.

Ví dụ: Escape ký tự đặc biệt

# Không escape, chữ khớp
"5a5".match(/\d.\d/)
# Chỉ literal dot khớp
"5a5".match(/\d\.\d/) # nil
"5.5".match(/\d\.\d/) # match

Modifiers (Điều Chỉnh)

Cho đến giờ chỉ khớp một ký tự. Để khớp nhiều ký tự dùng pattern modifiers.

ModifierMô tả
+1 hoặc nhiều
*0 hoặc nhiều
?0 hoặc 1
{3,5}giữa 3 và 5

Kết hợp mọi thứ học tạo regex phức tạp hơn.

Ví dụ: Có trông như địa chỉ IP?

# Lưu ý: cũng khớp IP không hợp lệ như 999.999.999.999, nhưng chỉ quan tâm format.
def ip_address?(str)
  # Dùng !! chuyển giá trị trả về boolean
  !!(str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
end
ip_address?("192.168.1.1") # true
ip_address?("0000.0000")   # false

Khớp Chuỗi Chính Xác

Cần khớp chính xác cần modifier khác. Xem ví dụ hiểu:

# Muốn tìm chuỗi đúng 4 ký tự, vẫn khớp vì có hơn 4, nhưng không muốn.
"Regex are cool".match /\w{4}/
# Thay vào đó dùng modifier 'đầu dòng' và 'cuối dòng'
"Regex are cool".match /^\w{4}$/
# Lần này không khớp. Ví dụ hơi gượng, có thể dùng .size độ dài, nhưng nghĩ nó truyền đạt ý tưởng.

Khớp chặt ở đầu chuỗi không phải mỗi dòng (sau \n) dùng \A và \Z thay ^ và $.

Capture Groups (Nhóm Bắt Giữ)

Với capture groups, bắt获 phần khớp và tái sử dụng sau. Để bắt获 đóng phần muốn trong ngoặc đơn.

Ví dụ: Parse file log

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.")
# Tạo đối tượng như: #

Ví dụ này dùng .match thay =~.

Phương thức trả về MatchData nếu khớp, nil ngược lại. Lớp MatchData có nhiều phương thức hữu ích, xem tài liệu!

Muốn giá trị boolean (true/false) dùng match? từ Ruby 2.4. Cũng nhanh hơn match vì không tạo MatchData.

Truy cập dữ liệu bắt获 dùng .captures hoặc đối tượng MatchData như mảng, index 0 khớp toàn bộ, index tiếp theo chứa nhóm khớp.

Muốn capture group đầu:

m = "John 31".match /\w+ (\d+)/
m[1] # 31

Cũng có non-capturing groups. Cho nhóm biểu thức không penalty hiệu năng. Named groups hữu ích làm biểu thức phức tạp dễ đọc.

Cú phápMô tả
(?:...)Non-capturing group
(?<foo>...)Named group

Ví dụ: Named Groups

m = "David 30".match /(?<name>\w+) (?<age>\d+)/
m[:age]  # => "30"
m[:name] # => "David"

Named group trả về MatchData truy cập đọc kết quả.

Look Ahead & Look Behind (Nhìn Trước & Nhìn Sau)

Kỹ thuật nâng cao có thể không có mọi regex implementation. Engine regex Ruby làm được, xem cách tận dụng.

Look ahead cho phép nhìn xem có khớp cụ thể trước/sau không.

TênMô tả
(?=pat)Positive lookahead
(?<=pat)Positive lookbehind
(?!pat)Negative lookahead
(?<!pat)Negative lookbehind

Ví dụ: Có số đứng sau ít nhất một chữ?

def number_after_word?(str)
  !!(str =~ /(?<=\w) (\d+)/)
end
number_after_word?("Grade 99")

Lớp Regexp Ruby

Ruby regular expressions là instance lớp Regexp. Thường không dùng trực tiếp lớp này, nhưng tốt biết 🙂

puts /a/.class # Regexp

Một dùng có thể: tạo regex từ chuỗi:

regexp = Regexp.new("a")

Cách khác tạo regexp:

regexp = %r{\w+}

Regex Options (Tùy Chọn)

Set tùy chọn trên regex làm hành xử khác.

OptionsMô tả
iruby regex case insensitive
mdot matches newline
xignore whitespace

Dùng tùy chọn thêm chữ cái cuối regex, sau / đóng.

Như sau:

"abc".match?(/[A-Z]/i)

Định Dạng Regex Dài

Ruby regex phức tạp khó đọc, hữu ích chia thành nhiều dòng. Làm bằng modifier 'x'. Format này cũng cho phép comment trong regex.

Ví dụ:

LOG_FORMAT = %r{
  (\d{2}:\d{2}) # Time
  \s(\w+)       # Event type
  \s(.*)        # Message
}x

Ruby Regex: Gộp Tất Cả

Regular expressions dùng với nhiều phương thức Ruby.

  • .split
  • .scan
  • .gsub
  • v.v…

Ví dụ: Khớp tất cả từ từ chuỗi dùng .scan

"this is some string".scan(/\w+/) # => ["this", "is", "some", "string"]

Ví dụ: Trích xuất tất cả số từ chuỗi

"The year was 1492.".scan(/\d+/) # => ["1492"]

Ví dụ: Viết hoa tất cả từ trong chuỗi

str = "lord of the rings"
str.gsub(/\w+/) { |w| w.capitalize } # => "Lord Of The Rings"

Ví dụ: Xác thực email

email = "test@example.com"
!!email.match(/\A[\w.+-]+@\w+\.\w+\z/) # true

Ví dụ cuối dùng !! chuyển kết quả thành boolean (true/false), thay thế dùng match? Ruby 2.4+ đã làm vậy & nhanh hơn.

Kết Luận

Regular expressions tuyệt vời nhưng đôi khi hơi tricky. Công cụ như rubular.com giúp xây ruby regex tương tác hơn. Rubular cũng có cheat sheet ruby regex rất hữu ích. Bây giờ lượt bạn mở editor code!

À, đừng quên chia sẻ này với bạn bè nếu thích, để nhiều người học 🙂