Python - Biểu thức Chính quy

Chào bạn, những phù thủy Python tương lai! Hôm nay, chúng ta sẽ bắt đầu hành trình thú vị vào thế giới của Biểu thức Chính quy (regex) trong Python. Đừng lo lắng nếu bạn chưa từng nghe về regex - đến cuối hướng dẫn này, bạn sẽ sử dụng công cụ mạnh mẽ này như một chuyên gia!

Python - Reg Expressions

Biểu thức Chính quy là gì?

Trước khi bắt đầu, hãy hiểu rõ về biểu thức chính quy. Hãy tưởng tượng bạn là một cảnh sát đang tìm kiếm một mẫu cụ thể trong một bộ văn bản lớn. Biểu thức chính quy như một kính lúp, giúp bạn tìm kiếm và thao tác các chuỗi dựa trên mẫu. Có khúc phải không?

Chuỗi Thô

Trong Python, khi làm việc với regex, chúng ta thường sử dụng chuỗi thô. Những chuỗi này có tiền tố là 'r' và coi dấu gạch ngang là ký tự định nghĩa. Điều này rất hữu ích trong regex vì dấu gạch ngang là phổ biến.

# Chuỗi bình thường
print("Hello\nWorld")
# Chuỗi thô
print(r"Hello\nWorld")

Trong trường hợp đầu tiên, bạn sẽ thấy "Hello" và "World" trên các dòng khác nhau. Trong trường hợp thứ hai, bạn sẽ thấy "Hello\nWorld" như được. Điều này rất quan trọng khi làm việc với mẫu regex.

Ký tự Đặc biệt

Ký tự đặc biệt là các khối xây dựng của regex. Chúng có ý nghĩa đặc biệt và giúp chúng ta định nghĩa các mẫu. Hãy xem một số ký tự đặc biệt phổ biến:

Ký tự Đặc biệt Ý nghĩa
. Khớp bất kỳ ký tự nào trừ newline
^ Khớp đầu của chuỗi
$ Khớp cuối của chuỗi
* Khớp 0 hoặc nhiều lần
+ Khớp 1 hoặc nhiều lần
? Khớp 0 hoặc 1 lần
{} Khớp số lần cụ thể
[] Chỉ định một tập hợp ký tự để khớp
\ Thoát các ký tự đặc biệt

Hàm re.match()

Hàm re.match() cố gắng khớp một mẫu ở đầu của chuỗi. Nếu nó tìm thấy khớp, nó trả về một đối tượng khớp; nếu không, nó trả về None.

import re

kết_quả = re.match(r"Hello", "Hello, World!")
if kết_quả:
print("Match found:", kết_quả.group())
else:
print("No match")

Điều này sẽ in "Match found: Hello". Phương thức group() trả về chuỗi khớp.

Hàm re.search()

Trong khi re.match() tìm kiếm khớp ở đầu của chuỗi, re.search() quét toàn bộ chuỗi để tìm kiếm khớp.

import re

kết_quả = re.search(r"World", "Hello, World!")
if kết_quả:
print("Match found:", kết_quả.group())
else:
print("No match")

Điều này sẽ in "Match found: World".

So sánh Khớp và Tìm kiếm

Sự khác biệt chính giữa match()search()match() chỉ kiểm tra khớp ở đầu của chuỗi, trong khi search() kiểm tra khớp bất cứ nơi nào trong chuỗi.

Hàm re.findall()

Hàm re.findall() trả về tất cả các khớp không trùng lặp của một mẫu trong chuỗi dưới dạng một danh sách.

import re

văn_bản = "The rain in Spain falls mainly in the plain"
kết_quả = re.findall(r"ain", văn_bản)
print(kết_quả)

Điều này sẽ in ['ain', 'ain', 'ain'].

Hàm re.sub()

Hàm re.sub() thay thế tất cả các xuất hiện của một mẫu trong chuỗi bằng một chuỗi thay thế.

import re

văn_bản = "The rain in Spain"
kết_quả = re.sub(r"a", "o", văn_bản)
print(kết_quả)

Điều này sẽ in "The roin in Spoin".

Hàm re.compile()

Hàm re.compile() tạo một đối tượng regex để sử dụng lại, điều này có thể là hiệu quả hơn nếu bạn sử dụng cùng một mẫu nhiều lần.

import re

mẫu = re.compile(r"\d+")
kết_quả1 = mẫu.findall("There are 123 apples and 456 oranges")
kết_quả2 = mẫu.findall("I have 789 bananas")

print(kết_quả1)
print(kết_quả2)

Điều này sẽ in ['123', '456']['789'].

Hàm re.finditer()

Hàm re.finditer() trả về một con trỏ tạo ra các đối tượng khớp cho tất cả các khớp không trùng lặp của một mẫu trong chuỗi.

import re

văn_bản = "The rain in Spain"
for khớp in re.finditer(r"ain", văn_bản):
print(f"Found '{khớp.group()}' at position {khớp.start()}-{khớp.end()}")

Điều này sẽ in:

Found 'ain' at position 5-8
Found 'ain' at position 17-20

Các Trường hợp Sử dụng của Python Regex

Biểu thức chính quy có nhiều ứng dụng thực tế. Hãy xem một trường hợp sử dụng phổ biến:

Tìm từ bắt đầu bằng nguyên âm

import re

văn_bản = "An apple a day keeps the doctor away"
từ_bắt đầu_bằng_nguyên âm = re.findall(r'\b[aeiouAEIOU]\w+', văn_bản)
print(từ_bắt đầu_bằng_nguyên âm)

Điều này sẽ in ['An', 'apple', 'a', 'away'].

Các Biểu thức Chính quy: Các Cờ Tùy chọn

Mô-đun Python cung cấp một số cờ tùy chọn nhận diện cách các mẫu được phân tích:

Cờ Mô tả
re.IGNORECASE (re.I) Thực hiện khớp không phân biệt hoa thường
re.MULTILINE (re.M) Làm cho ^ khớp đầu của mỗi dòng và $ cuối của mỗi dòng
re.DOTALL (re.S) Làm cho . khớp bất kỳ ký tự nào, bao gồm newline
re.VERBOSE (re.X) Cho phép bạn viết các mẫu regex dễ đọc hơn

Các Mẫu Biểu thức Chính quy

Hãy khám phá một số mẫu phức tạp hơn:

Các lớp Ký tự

Các lớp ký tự cho phép bạn chỉ định một tập hợp ký tự để khớp:

import re

văn_bản = "The quick brown fox jumps over the lazy dog"
kết_quả = re.findall(r"[aeiou]", văn_bản)
print(kết_quả)

Điều này sẽ in tất cả các nguyên âm được tìm thấy trong văn bản.

Các lớp Ký tự Đặc biệt

Python regex hỗ trợ các lớp ký tự đặc biệt:

Lớp Mô tả
\d Khớp bất kỳ chữ số thập phân nào
\D Khớp bất kỳ ký tự không phải là chữ số nào
\s Khớp bất kỳ ký tự trắng nào
\S Khớp bất kỳ ký tự không trắng nào
\w Khớp bất kỳ ký tự chữ cái hoặc số nào
\W Khớp bất kỳ ký tự không phải là chữ cái hoặc số nào

Các Trường hợp Lặp lại

Chúng ta có thể chỉ định số lần một mẫu phải xảy ra:

import re

văn_bản = "I have 111 apples and 22 oranges"
kết_quả = re.findall(r"\d{2,3}", văn_bản)
print(kết_quả)

Điều này sẽ in ['111', '22'], khớp các số có 2 hoặc 3 chữ số.

Lặp lại không tham vọng

Bằng mặc định, lặp lại là tham vọng, có nghĩa là nó khớp càng nhiều càng tốt. Thêm một ? sau lặp lại làm cho nó không tham vọng:

import re

văn_bản = "<h1>Title</h1><p>Paragraph</p>"
tham_vọng = re.findall(r"<.*>", văn_bản)
không_tham_vọng = re.findall(r"<.*?>", văn_bản)
print("Greedy:", tham_vọng)
print("Non-greedy:", không_tham_vọng)

Điều này sẽ hiển thị sự khác biệt giữa lặp lại tham vọng và không tham vọng.

Nhóm với Dấu ngoặc đơn

Dấu ngoặc đơn cho phép bạn nhóm các phần của regex:

import re

văn_bản = "John Smith ([email protected])"
kết_quả = re.search(r"(\w+) (\w+) \((\w+@\w+\.\w+)\)", văn_bản)
if kết_quả:
print(f"Full Name: {kết_quả.group(1)} {kết_quả.group(2)}")
print(f"Email: {kết_quả.group(3)}")

Điều này trích xuất tên và email từ văn bản.

Quay lại tham chiếu

Quay lại tham chiếu cho phép bạn đề cập lại các nhóm đã khớp:

import re

văn_bản = "<h1>Title</h1><p>Paragraph</p>"
kết_quả = re.findall(r"<(\w+)>.*?</\1>", văn_bản)
print(kết_quả)

Điều này khớp các thẻ mở và đóng HTML.

Các lựa chọn

Dấu | cho phép bạn chỉ định các lựa chọn:

import re

văn_bản = "The color of the sky is blue or gray"
kết_quả = re.search(r"blue|gray", văn_bản)
if kết_quả:
print(f"Found color: {kết_quả.group()}")

Điều này khớp cả "blue" và "gray".

Các Dấu neo

Các dấu neo xác định các vị trí trong văn bản:

import re

văn_bản = "Python is awesome"
start = re.match(r"^Python", văn_bản)
end = re.search(r"awesome$", văn_bản)
print(f"Starts with Python: {bool(start)}")
print(f"Ends with awesome: {bool(end)}")

Điều này kiểm tra xem văn bản có bắt đầu bằng "Python" và kết thúc bằng "awesome".

Cú pháp đặc biệt với Dấu ngoặc đơn

Dấu ngoặc đơn có thể được sử dụng cho nhiều hơn chỉ nhóm:

  • (?:...) tạo một nhóm không thu thập
  • (?P...) tạo một nhóm có tên
  • (?=...) tạo một định dạng xem trước tích cực
  • (?!...) tạo một định dạng xem trước tiêu cực
import re

văn_bản = "Python version 3.9.5"
kết_quả = re.search(r"Python (?:version )?(?P<version>\d+\.\d+\.\d+)", văn_bản)
if kết_quả:
print(f"Version: {kết_quả.group('version')}")

Điều này trích xuất số phiên bản, dù "version" có hoặc không có trong văn bản.

Và đó là những gì bạn cần biết, folks! Chúng ta đã đi qua hành trình thú vị vào thế giới của Python regex, từ cơ bản đến một số khá phức tạp. Nhớ rằng, như bất kỳ công cụ mạnh mẽ nào, regex cần thực hành để thành thạo. Vì vậy đừng lo lắng nếu cảm thấy quá khó khăn ban đầu. Tiếp tục thử nghiệm và sớm nhất bạn sẽ trở thành một chiến binh regex như một cảnh sát pro! Chúc bạn có những giờ编程 thú vị!

Credits: Image by storyset