2.35. Cơ bản về mẫu¶
Biểu thức chính quy là một ngôn ngữ nhỏ để mô tả chuỗi theo hình thức thay vì theo nội dung chính xác của chúng. Hãy dùng nó khi một chuỗi khớp khi và chỉ khi nó tuân theo một mẫu mà bạn có thể mô tả nhưng không thể liệt kê -- "một dãy chữ số theo sau là một đơn vị", "một dòng bắt đầu bằng ERROR và kết thúc bằng một số", "bất kỳ phần mở rộng tệp nào trong số này, theo thứ tự bất kỳ, với tiền tố v tùy chọn".
Chỉ sử dụng re khi một phương thức chuỗi thuần không đáp ứng được.
str.startswith(),str.endswith()-- kiểm tra tiền tố hoặc hậu tố cố định.in-- kiểm tra xem một chuỗi con cố định có tồn tại không.str.split(),str.find(),str.replace()-- làm việc với các dấu phân cách cố định.
Mỗi phương thức đó nhanh hơn, dễ đọc hơn và ít sai hơn so với regex tương đương. Dùng regex khi hình thức của chuỗi quan trọng và chuỗi con chính xác thì không.
2.35.1. Bốn thứ bạn sẽ dùng¶
Module MicroPython re cung cấp bốn thứ:
re.compile()-- biến một chuỗi mẫu thành đối tượng mẫu đã biên dịch mà bạn có thể dùng lại.re.match()-- thử mẫu tại đầu của một chuỗi. Mẫu được neo tại vị trí 0.re.search()-- thử mẫu bất kỳ đâu trong một chuỗi. Trả về kết quả khớp đầu tiên.re.sub()-- tìm mọi kết quả khớp và thay thế chúng.
Các thiếu sót đáng chú ý so với CPython: không có re.findall, không có re.finditer, không có re.split ở cấp module (các mẫu đã biên dịch có phương thức split thay thế), không có re.fullmatch, không có hằng số cờ như re.IGNORECASE. Khi bạn muốn dùng một trong những thứ đó trên CPython, hãy xây dựng tương đương từ re.search() trong một vòng lặp.
2.35.2. Mẫu đầu tiên¶
Mẫu r'\d+' khớp với một hoặc nhiều chữ số:
>>> import re
>>> m = re.search(r'\d+', 'sensor reading 42 ok')
>>> m.group(0)
'42'
Một vài điểm cần chú ý:
Mẫu được viết dưới dạng chuỗi thô (
r'...') để dấu gạch chéo ngược trong\dtruyền đếnrethay vì bị xử lý như ký tự thoát chuỗi Python. Luôn dùng chuỗi thô cho mẫu regex.re.search()trả về đối tượng khớp khi thành công vàNonekhi thất bại. Luôn kiểm tra trước khi gọimatch.group().m.group(0)là toàn bộ văn bản mà mẫu đã khớp. Nhóm 1, 2, ... xuất hiện sau, khi mẫu có chứa dấu ngoặc bắt giữ.
Cùng mẫu với re.match() trả về None vì chuỗi không bắt đầu bằng chữ số:
>>> re.match(r'\d+', 'sensor reading 42 ok') is None
True
>>> re.match(r'\d+', '42 readings')
<match num=1>
2.35.3. Các thành phần của một mẫu¶
Hầu hết các mẫu hữu ích được xây dựng từ một tập nhỏ các thành phần. Những thành phần hoạt động trong MicroPython:
Ký tự literal -- bất kỳ ký tự nào không đặc biệt đều khớp với chính nó. hello khớp với hello.
Ký tự đặc biệt -- . ^ $ * + ? { } [ ] \ | ( ) đều có ý nghĩa bên dưới. Để khớp một trong số chúng theo nghĩa đen, hãy thoát nó bằng dấu gạch chéo ngược: \. khớp với một dấu chấm literal.
Lớp ký tự -- phím tắt cho các tập ký tự phổ biến:
\d-- bất kỳ chữ số nào từ0-9\D-- bất kỳ ký tự nào không phải chữ số\s-- bất kỳ ký tự khoảng trắng nào (dấu cách, tab, xuống dòng)\S-- bất kỳ ký tự nào không phải khoảng trắng\w-- bất kỳ ký tự "từ" nào: chữ cái, chữ số, dấu gạch dưới\W-- bất kỳ ký tự nào không phải ký tự từ.-- bất kỳ ký tự nào ngoại trừ dòng mới
Bộ định lượng -- số lần thành phần trước phải khớp:
*-- không hoặc nhiều lần (tham lam)+-- một hoặc nhiều lần (tham lam)?-- không hoặc một lần{n}-- chính xác n lần{m,n}-- giữa m và n lần (bao gồm hai đầu)
Kết hợp: \d{3}-\d{4} khớp với ba chữ số, một dấu gạch ngang, bốn chữ số. \s+ khớp với một hoặc nhiều ký tự khoảng trắng. hello.*world khớp với hello, bất kỳ thứ gì (kể cả không có gì), rồi world.
Ghi chú
Tham lam có nghĩa là bộ định lượng tiêu thụ nhiều đầu vào nhất có thể trong khi vẫn cho phép phần còn lại của mẫu khớp. Với hello x world y world, .* trong hello.*world khớp với đoạn dài nhất vẫn còn world ở cuối -- nó bắt x world y, không phải x ngắn hơn. Điều tương tự đúng với + và dạng phạm vi {m,n}: engine lấy kết quả khớp dài nhất có thể, rồi lùi lại chỉ khi phần còn lại của mẫu thất bại.
2.35.4. Thay thế¶
re.sub() tìm mọi kết quả khớp và thay thế chúng bằng một chuỗi. Chuỗi thay thế có thể tham chiếu các nhóm bắt giữ qua \1, \2, ... (được đề cập cùng với phần còn lại của cú pháp nhóm sau). Không có nhóm, re.sub là tìm và thay thế đơn giản trên một regex:
>>> re.sub(r'\s+', ' ', 'too many spaces')
'too many spaces'
>>> re.sub(r'\d+', 'N', 'log 12, log 345, log 6')
'log N, log N, log N'
Tham số thứ ba là chuỗi cần thao tác; kết quả là một chuỗi mới với mọi kết quả khớp được thay thế.
2.35.5. Tách chuỗi -- chỉ trên mẫu đã biên dịch¶
Không có re.split ở cấp module. Để tách theo regex, hãy biên dịch mẫu trước rồi gọi phương thức split của nó:
>>> sep = re.compile(r'\s*,\s*')
>>> sep.split('a , b,c , d')
['a', 'b', 'c', 'd']
Tham số thứ hai tùy chọn giới hạn số lần tách:
>>> sep.split('a, b, c, d', 2)
['a', 'b', 'c, d']
2.35.6. Biên dịch để tái sử dụng¶
Nếu cùng một mẫu chạy nhiều lần -- bên trong một vòng lặp hoặc trong một hàm được gọi nhiều -- hãy biên dịch nó một lần và tái sử dụng đối tượng đã biên dịch:
digit_run = re.compile(r'\d+')
def first_number(line):
m = digit_run.search(line)
return int(m.group(0)) if m else None
Gọi pattern.match() và pattern.search() trên đối tượng đã biên dịch giống như các hàm cấp module nhưng bỏ qua chi phí biên dịch lại trong mỗi lần gọi.
2.35.7. Các mẫu không khớp bất kỳ thứ gì¶
Ba mẫu cụ thể thường gây nhầm lẫn cho các lập trình viên:
.*khớp với chuỗi rỗng.re.search(r'.*', s).group(0)trả về''với bất kỳ đầu vào nào.Một mẫu có ký tự đặc biệt không được thoát là lỗi cú pháp.
re.compile(r'cost: $5')gây raValueErrorvì$có nghĩa là "cuối chuỗi". Dùngr'cost: \$5'.Dấu chấm
.không khớp với dòng mới. Để khớp qua các dòng mới, hãy viết mẫu để xử lý chúng tường minh với[\s\S]hoặc đưa vào từng dòng một.
Với những thành phần này, một mẫu có thể khớp với hầu như bất kỳ đoạn văn bản có dạng cố định nào. Để trích xuất dữ liệu có cấu trúc từ kết quả khớp, cần sử dụng nhóm bắt giữ.