Computer >> Máy Tính >  >> Lập trình >> Ruby

Làm chủ các biểu thức chính quy của Ruby

Biểu thức chính quy Ruby ( ruby ​​regex gọi tắt là) giúp bạn tìm thấy các mẫu cụ thể bên trong chuỗi, với mục đích trích xuất dữ liệu để xử lý thêm.

Hai trường hợp sử dụng phổ biến cho biểu thức chính quy bao gồm xác thực và phân tích cú pháp.

Ví dụ :

Hãy nghĩ về một địa chỉ email, với ruby ​​regex bạn có thể xác định địa chỉ email hợp lệ trông như thế nào. Nói cách khác, chương trình của bạn sẽ có thể phân biệt được sự khác biệt giữa địa chỉ email hợp lệ và không hợp lệ.

Làm chủ các biểu thức chính quy của Ruby

Biểu thức chính quy Ruby được xác định giữa hai dấu gạch chéo về phía trước để phân biệt chúng với các cú pháp ngôn ngữ khác. Các biểu thức đơn giản nhất phù hợp với một từ hoặc thậm chí một chữ cái.

Ví dụ :

# Find the word 'like'
"Do you like cats?" =~ /like/

Điều này trả về chỉ mục của lần xuất hiện đầu tiên của từ nếu nó được tìm thấy (đối sánh thành công) hoặc nil nếu không thì. Nếu chúng ta không quan tâm đến chỉ mục, chúng ta có thể sử dụng Chuỗi # bao gồm không? phương pháp.

Một cách khác để kiểm tra xem một chuỗi có khớp với regex hay không là sử dụng match phương pháp:

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

Bây giờ:

Bạn sẽ học cách xây dựng các mẫu nâng cao hơn để bạn có thể đối sánh, nắm bắt và thay thế những thứ như ngày tháng, số điện thoại, email, URL, v.v.

Các lớp ký tự

Một lớp ký tự cho phép bạn xác định một phạm vi hoặc danh sách các ký tự để đối sánh. Ví dụ:[aeiou] khớp với bất kỳ nguyên âm nào.

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

def contains_vowel(str)
  str =~ /[aeiou]/
end

contains_vowel("test") # returns 1
contains_vowel("sky")  # returns nil

Điều này sẽ không tính đến số tiền trong số các ký tự, chúng tôi sẽ sớm xem cách thực hiện điều đó.

Dải

Chúng tôi có thể sử dụng các phạm vi để khớp nhiều chữ cái hoặc số mà không cần phải nhập hết chúng. Nói cách khác, một phạm vi như [2-5] giống với [2345] .

Một số phạm vi hữu ích:

  • [0-9] khớp với bất kỳ số nào từ 0 đến 9
  • [a-z] khớp với bất kỳ chữ cái nào từ a đến z (không viết hoa)
  • [^ a-z] phạm vi phủ định

Ví dụ :Chuỗi này có chứa bất kỳ số nào không?

def contains_number(str)
  str =~ /[0-9]/
end

contains_number("The year is 2015")  # returns 12
contains_number("The cat is black")  # returns nil

Hãy nhớ:giá trị trả về khi sử dụng `=~` là chỉ số chuỗi hoặc `nil`

Có một cú pháp viết tắt rất hay để chỉ định phạm vi ký tự:

  • \ w tương đương với [0-9a-zA-Z_]
  • \ d giống với [0-9]
  • \ s khớp với khoảng trắng (tab, khoảng trắng thông thường, dòng mới)

Ngoài ra còn có dạng phủ định của những điều này:

  • \ W bất kỳ thứ gì không có trong [0-9a-zA-Z_]
  • \ D bất kỳ thứ gì không phải là số
  • \ S bất cứ thứ gì không phải là không gian

Ký tự dấu chấm . phù hợp với mọi thứ trừ các dòng mới. Nếu bạn cần sử dụng . theo nghĩa đen thì bạn sẽ phải thoát khỏi nó.

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

# 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

Công cụ sửa đổi

Cho đến nay, chúng tôi chỉ có thể ghép một ký tự duy nhất tại một thời điểm. Để khớp nhiều ký tự, chúng ta có thể sử dụng các công cụ sửa đổi mẫu.

Công cụ sửa đổi Mô tả
+ 1 hoặc nhiều hơn
* 0 trở lên
? 0 hoặc 1
{3,5} từ 3 đến 5

Chúng tôi có thể kết hợp mọi thứ chúng tôi đã học cho đến nay để tạo ra các biểu thức chính quy phức tạp hơn.

Ví dụ :Địa chỉ này có giống địa chỉ IP không?

# 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

Khớp chuỗi chính xác

Nếu bạn cần kết hợp chính xác, bạn sẽ cần một loại bổ trợ khác. Hãy xem một ví dụ để bạn có thể thấy những gì tôi đang nói về:

# 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.

Để khớp đúng ở phần đầu của một chuỗi chứ không chỉ ở mỗi dòng (sau \n ) bạn cần sử dụng \A\Z thay vì ^$ .

Chụp nhóm

Với các nhóm chụp, chúng tôi có thể chụp một phần của trận đấu và sử dụng lại sau đó. Để nắm bắt một kết quả phù hợp, chúng tôi đặt phần chúng tôi muốn ghi lại trong dấu ngoặc đơn.

Ví dụ :Phân tích cú pháp tệp nhật ký

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:
# 

Trong ví dụ này, chúng tôi đang sử dụng .match thay vì =~ .

Phương thức này trả về một MatchData đối tượng nếu có một kết quả phù hợp, nil nếu không. MatchData lớp có nhiều phương thức hữu ích, hãy xem tài liệu để tìm hiểu thêm!

Nếu bạn chỉ muốn một giá trị boolean (true / false ) thì bạn có thể sử dụng match? , có sẵn kể từ Ruby 2.4. Điều này cũng nhanh hơn so với match vì Ruby không cần tạo MatchData đối tượng.

Bạn có thể truy cập dữ liệu đã chụp bằng .captures hoặc xử lý MatchData đối tượng như một mảng, chỉ số 0 sẽ có toàn bộ kết quả phù hợp và các chỉ mục kết quả sẽ chứa các nhóm được so khớp.

Nếu bạn muốn nhóm chụp đầu tiên, bạn có thể thực hiện điều này:

m = "John 31".match /\w+ (\d+)/

m[1]
# 31

Bạn cũng có thể có các nhóm không chụp. Họ sẽ cho phép bạn nhóm các biểu cảm lại với nhau mà không bị phạt. Bạn cũng có thể thấy các nhóm được đặt tên hữu ích để làm cho các biểu thức phức tạp dễ đọc hơn.

Cú pháp Mô tả
(?:...) Nhóm không chụp
(?<foo>...) Nhóm được đặt tên

Ví dụ :Nhóm được đặt tên

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

Một nhóm được đặt tên trả về một MatchData đối tượng mà bạn có thể truy cập để đọc kết quả.

Nhìn trước &Nhìn sau

Đây là một kỹ thuật nâng cao hơn có thể không có sẵn trong tất cả các triển khai regex. Công cụ biểu thức chính quy của Ruby có thể thực hiện điều này, vì vậy hãy xem cách tận dụng điều đó.

Nhìn về phía trước cho phép chúng tôi xem xét và xem liệu có một trận đấu cụ thể trước hay sau.

Tên Mô tả
(? =pat) Cái nhìn tích cực
(? <=pat) Cái nhìn tích cực
(?! pat) Cái nhìn phủ định
(? Cái nhìn tiêu cực

Ví dụ :có một số đứng trước ít nhất một chữ cái không?

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

number_after_word?("Grade 99")

Lớp Regex của Ruby

Biểu thức chính quy Ruby là các bản sao của Regexp lớp. Hầu hết thời gian bạn sẽ không sử dụng trực tiếp lớp học này, nhưng thật tốt khi biết 🙂

puts /a/.class
# Regexp

Một cách sử dụng có thể có là tạo regex từ một chuỗi:

regexp = Regexp.new("a")

Một cách khác để tạo regexp:

regexp = %r{\w+}

Tùy chọn Regex

Bạn có thể đặt một số tùy chọn trên biểu thức chính quy của mình để làm cho nó hoạt động khác.

Tùy chọn Mô tả
tôi ruby ​​regex không phân biệt chữ hoa chữ thường
m chấm khớp với dòng mới
x bỏ qua khoảng trắng

Để sử dụng các tùy chọn này, bạn thêm ký tự vào cuối regex, sau dấu đóng / .

Như thế này :

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

Định dạng Cụm từ Thông dụng Dài

Các biểu thức chính quy phức tạp của Ruby có thể khá khó đọc, vì vậy sẽ rất hữu ích nếu chúng ta chia chúng thành nhiều dòng. Chúng ta có thể thực hiện điều này bằng cách sử dụng công cụ sửa đổi ‘x’. Định dạng này cũng cho phép bạn sử dụng các nhận xét bên trong regex của mình.

Ví dụ :

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

Ruby regex:Kết hợp tất cả lại với nhau

Các biểu thức chính quy có thể được sử dụng với nhiều phương thức Ruby.

  • .split
  • .scan
  • .gsub
  • và nhiều hơn nữa…

Ví dụ :Khớp tất cả các từ từ một chuỗi bằng cách sử dụng .scan

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

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

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

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

str = "lord of the rings"

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

Ví dụ :Xác thực địa chỉ email

email = "test@example.com"

!!email.match(/\A[\w.+-]+@\w+\.\w+\z/)

# true

Ví dụ cuối cùng này sử dụng !! để chuyển đổi kết quả thành giá trị boolean (true / false ), hoặc bạn có thể sử dụng match? trong Ruby 2.4+ đã thực hiện điều này cho bạn và nó cũng nhanh hơn.

Kết luận

Biểu thức chính quy rất tuyệt vời nhưng đôi khi chúng có thể hơi phức tạp. Sử dụng một công cụ như rubular.com có ​​thể giúp bạn xây dựng ruby ​​regex của mình theo một cách tương tác hơn. Rubular cũng bao gồm một bảng gian lận biểu thức chính quy Ruby mà bạn sẽ thấy rất hữu ích. Bây giờ đến lượt bạn mở trình chỉnh sửa đó và bắt đầu viết mã!

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