Menu

Regex trong Python: module re, mẫu, group và thay thế chuỗi

Làm quen thực hành với module re của Python: tìm kiếm, so khớp, bắt group, thay thế và những mẫu regex bạn sẽ dùng nhiều nhất.

Trang này có trình soạn thảo chạy trực tiếp. Sửa, chạy và xem đầu ra ngay lập tức.

Khi nào nên dùng regex

Biểu thức chính quy (regular expression) là một ngôn ngữ nhỏ để mô tả mẫu văn bản. Chúng rất mạnh và cũng rất dễ bị lạm dụng.

Trước khi dùng re, hãy tự hỏi liệu các phương thức chuỗi thông thường có đủ không. .split(), .replace(), .startswith(), "target" in text: những cách này nhanh hơn, dễ đọc hơn và ít lỗi hơn regex tương đương. Hãy để dành regex cho lúc mẫu bạn quan tâm thực sự có cấu trúc nhưng quá linh hoạt cho các thao tác chuỗi cố định: địa chỉ email, số điện thoại, dòng log có định dạng biết trước, đoạn HTML hoặc Markdown, mọi kiểu tìm kiếm "tìm loại thứ này".

Từ vựng cơ bản

Một mẫu (pattern) regex là một chuỗi mô tả thứ bạn đang tìm. Vài khối xây dựng:

  • .: một ký tự bất kỳ
  • \d: một chữ số bất kỳ (từ 0 đến 9)
  • \w: một ký tự "từ" bất kỳ (chữ cái, chữ số, dấu gạch dưới)
  • \s: một ký tự khoảng trắng bất kỳ
  • ^: đầu chuỗi
  • $: cuối chuỗi
  • [abc]: một trong các ký tự a, b hoặc c
  • [^abc]: mọi ký tự trừ a, b, c
  • a|b: a hoặc b
  • *: không hoặc nhiều lần thứ đứng trước
  • +: một hoặc nhiều lần
  • ?: không hoặc một lần
  • {3}: đúng 3 lần
  • {2,5}: từ 2 đến 5 lần
  • ( ... ): group (bắt lấy phần khớp bên trong)

Chừng đó đủ cho phần lớn regex ngoài thực tế.

Các hàm chính

re.search(pattern, text) tìm chỗ khớp đầu tiên ở bất kỳ đâu trong chuỗi. Trả về một đối tượng match hoặc None:

Luôn dùng raw string (r"...") cho mẫu regex. Nếu không, Python sẽ cố hiểu các dấu gạch chéo ngược là ký tự thoát của chuỗi và bạn nhận được một mẫu khác với thứ đã gõ.

re.match(pattern, text) giống search nhưng chỉ khớp ở đầu chuỗi:

Phần lớn thời gian, bạn cần search.

re.findall(pattern, text) trả về mọi chỗ khớp không chồng lấn nhau dưới dạng list:

Lưu ý findall trả về chuỗi, không phải đối tượng match. Nếu mẫu có một group, bạn nhận lại nội dung của group đó. Với nhiều group, bạn nhận được một list các tuple.

Capturing group

Dấu ngoặc tròn trong mẫu bắt lấy bất cứ thứ gì khớp bên trong chúng:

Group có tên (named group) thường rõ ràng hơn:

Khi regex có nhiều hơn một group, đặt tên cho chúng giúp code trích xuất dễ theo dõi hơn nhiều.

Thay thế với re.sub

re.sub(pattern, replacement, text) thay thế mọi chỗ khớp:

Đoạn này bỏ đi mọi thứ không phải chữ số. Chuỗi thay thế có thể tham chiếu đến các group đã bắt bằng \1, \2, v.v., hoặc trong raw string thì \g<1> rõ ràng hơn:

Bạn cũng có thể truyền một hàm làm phần thay thế. Cách này hữu ích cho những phép biến đổi không chỉ là tráo đổi đơn giản:

Biên dịch mẫu để dùng lại

Nếu bạn dùng cùng một mẫu nhiều lần, nhất là trong vòng lặp, hãy biên dịch nó một lần:

Mẫu đã biên dịch có cùng các phương thức search, match, findall, sub, chỉ là không cần truyền mẫu làm đối số đầu tiên. Hiệu quả hơn một chút, và thường dễ đọc hơn.

Cờ (flag)

Các tùy chọn hay dùng, truyền qua đối số flags= hoặc kết hợp bằng phép OR:

  • re.IGNORECASE (re.I): so khớp không phân biệt chữ hoa chữ thường.
  • re.MULTILINE (re.M): ^ và $ khớp ở mỗi dòng, không chỉ ở đầu và cuối chuỗi.
  • re.DOTALL (re.S): . khớp cả ký tự xuống dòng (mặc định thì không).
  • re.VERBOSE (re.X): cho phép khoảng trắng và chú thích # trong mẫu để dễ đọc.

re.VERBOSE đặc biệt hữu ích với các mẫu phức tạp:

Regex nhiều dòng có chú thích dễ bảo trì hơn hẳn những dòng một khó hiểu.

Greedy và lazy

Các quantifier (*, +, ?, {n,}) mặc định là greedy (tham lam): chúng khớp nhiều nhất có thể. Thêm ? để biến chúng thành lazy (lười):

Bản greedy bắt cả chuỗi con từ <b> đến </i>, có lẽ không phải điều bạn muốn. Bản lazy .+? dừng ở dấu > đầu tiên.

(Lưu ý thêm: đừng thực sự phân tích HTML bằng regex trong môi trường production. Hãy dùng html.parser hoặc BeautifulSoup. Ví dụ trên chỉ để minh họa tính greedy.)

Một ví dụ thực tế

Phân tích các dòng từ một định dạng log đơn giản:

Rất nhiều sức mạnh chỉ trong vài dòng code.

Vài thói quen tốt

  • Luôn dùng raw string cho mẫu.
  • Bắt đầu bằng mẫu đơn giản nhất chạy được; siết chặt dần sau.
  • Dùng named group khi có nhiều hơn một group.
  • Biên dịch những mẫu sẽ dùng lại.
  • Regex chậm hơn phương thức chuỗi thông thường. Nếu .split() làm được, hãy dùng .split().

Tiếp theo: lỗi và gỡ lỗi

Đến đây là hết phần làm việc với dữ liệu thực tế: tệp, JSON, CSV, HTTP, ngày giờ và regex. Tuy nhiên, chương trình thực tế nào sớm muộn cũng gặp lỗi, và đọc tốt traceback của Python là kỹ năng gỡ lỗi quan trọng nhất bạn có thể học. Chương cuối nói về ngoại lệ và những lỗi cụ thể bạn sẽ gặp thường xuyên nhất.

Câu hỏi thường gặp

Regex trong Python là gì?

Biểu thức chính quy (regular expression, regex) là một ngôn ngữ nhỏ để mô tả các mẫu trong văn bản. Module re của Python cho phép bạn tìm mẫu, trích xuất từng phần và thay thế các chỗ khớp. Với thao tác chuỗi đơn giản thì regex là quá mức cần thiết, hãy dùng phương thức chuỗi như .split() hay .replace() trước, nhưng với việc so khớp mẫu có cấu trúc thì regex không có đối thủ.

re.match và re.search khác nhau thế nào?

re.match chỉ khớp ở đầu chuỗi. re.search quét cả chuỗi và tìm chỗ khớp đầu tiên ở bất kỳ đâu. Khi phân vân, hãy dùng search vì nó gần với trực giác của con người hơn.

Có nên luôn dùng raw string cho mẫu regex không?

Có. Mẫu regex thường chứa dấu gạch chéo ngược (\d, \s, \b), mà chuỗi Python lại hiểu chúng là chuỗi thoát (escape sequence). Thêm tiền tố r, như r'\d+', bảo Python giữ nguyên chuỗi theo nghĩa đen, giúp bản thân regex dễ đọc.

Hình minh họa các ngôn ngữ lập trình của Coddy

Học lập trình cùng Coddy

Bắt đầu