Các thủ thuật với Regular Expression
Bài đăng này đã không được cập nhật trong 8 năm
Regular Repression (regex) là một công cụ mạnh trong việc xử lý xâu. Sử dụng regex sẽ giúp tránh được việc phải viết các hàm kiểm tra, các câu điều kiện dài. Nhờ thế, regex đặc biệt phù hợp vói Ruby, một ngôn ngữ có cú pháp ngắn gọn. Sau đây là một số thủ thuật sử dụng regex trong code Ruby để làm cho code của bạn còn ngắn gọn và sáng sủa hơn nữa.
Split xâu bằng regex
Mọi Ruby coder đều biết cách sử dụng hàm split như sau:
"one,two".split ","
=> ["one", "two"]
Nhưng không phải ai cũng biết rằng hàm split
này nhận cả regex
"one,two-three".split /,|-/
=> ["one", "two", "three"]
Thậm chí, ta có thể tách xâu và lấy luôn cả các kí hiệu phân cách vào trong dãy mới sinh ra, một việc dường như không tưởng với hàm split
. Tất cả công việc cần làm là nhóm các kí hiệu phân cách lại với nhau:
"one,two-three".split /(,|-)/
=> ["one", ",", "two", "-", "three"]
Lý do bởi vì thực ra hàm split
được cài đặt để cắt xâu ở biên của mỗi nhóm mà nó bắt được.
Dựa vào đặc tính trên, ta có thể khiến cho hàm split
hoạt động gần giống như match
.
"1-800-555-1212".split /(1)-(\d{3})-(\d{3})-(\d{4})/
=> ["", "1", "800", "555", "1212"]
Matching với regex
Bình thường hàm match
chỉ cho ra kết quả đầu tiên tìm được:
"12345".match /\d/
=> #<MatchData "1">
Để cho ra tất cả các kết quả tìm được, ta có thể dùng hàm scan
:
"12345".scan /\d/
=> ["1", "2", "3", "4", "5"]
Ngoài ra, ta có thể dùng toán tử ===
để match:
/hi/ === "hiho"
# true
Toán tử ===
được cài đặt sử dụng trong cấu trúc case
của Ruby, vì vậy ta hoàn toàn có thể sử dụng regex trong cấu trúc này:
case "hiho"
when /hi/
puts "match"
end
Toán tử =~
cũng là một cách để kiểm tra match. Kết quả tra về là số thứ tự xuất hiện lần đầu của regex:
"hiho" =~ /hi/
# 0
"hiho" =~ /ho/
# 2
Sử dụng nhiều nhóm trong regex để match được nhiều kết quả hơn:
m = "Framgia, Vietnam".match /([^,]+)(, *)([A-Z])/
# => #<MatchData "Framgia, Vietnam" 1:"Framgia" 2:", " 3:"Vietnam">
m[1]
# Framgia
m[3]
# Vietnam
Ta có thể đặt tên cho các nhóm trong regex để giúp cho kết quả hàm match
được "đẹp" hơn, dễ dàng cho việc truy cập và xử lý. Cú pháp đặt tên nhóm như sau:
/(?<groupname>regex)/
Ví dụ:
m = "Framgia, Vietnam".match /(?<company>[^,]+), *(?<country>[A-Z])/
# => #<MatchData "Framgia, Vietnam" company:"Framgia" country:"Vietnam">
m[:company]
# Framgia
m[:country]
# Vietnam
Kết quả thu được có thể sử dụng như một hash.
Sử dụng điều kiện trong regex
Biểu thức điều kiện trong regex có dạng /(?(A)X|Y)/
, trong đó A
là điều kiện, X
là kết quả nếu A
đúng, Y
là kết quả nếu A
sai. Biểu thức có thể khuyết X
hoặc Y
. Ví dụ:
re = /^(1-)?(?(1)\d{3}-|(\d{3}-)?)\d{3}-\d{4}/
"1-800-555-1212".match re
#=> #<MatchData "1-800-555-1212" 1:"1-" 2:nil>
"800-555-1212".match re
#=> #<MatchData "800-555-1212" 1:nil 2:"800-">
"555-1212".match re
#=> #<MatchData "555-1212" 1:nil 2:nil>
"1-555-1212".match re
=> nil
Regex re
ở ví dụ trên nhìn qua trông có vẻ rất phức tạp, nhưng rất dễ hiểu nếu ta chia nhỏ ra thành từng phần:
^(1-)?
: Kiểm tra xâu có bắt đầu với "1-" không. Nếu có đưa vào nhóm 1(?(1)
: Biểu thức điều kiện kiểm tra xâu có trong nhóm 1 không\d{3}-
: Nếu xâu nằm trong nhóm 1, kiểm tra tiếp theo có phải là 3 chữ số và dấu "-" không|(\d{3}-)?
: Nếu không nằm trong nhóm 1, kiểm tra xem xâu có bắt đầu bằng 3 chữ số và dấu "-" không\d{3}-\d{4}
: Kiểm tra phần tiếp theo có phải là 3 chữ số, dấu "-" và 4 chữ số không, phần này hoạt động như regex bình thường
All rights reserved