Regular Expression (RegEx) trong Python

Chương tiếp theo của bài học nâng cao Python, Quantrimang.com sẽ cùng bạn tìm hiểu về Regular Expression (RegEx) với module re cùng các ví dụ cụ thể để bạn dễ hình dung và nắm bắt kĩ hơn kiến thức về RegEx. Cùng theo dõi nhé!

Regular Expression (RegEx) hay còn gọi là Biểu thức chính quy là một đoạn các ký tự đặc biệt theo những khuôn mẫu (pattern) nhất định, đại diện cho chuỗi hoặc một tập các chuỗi. Ví dụ:

^a...s$

Đoạn code trên xác định quy tắc RegEx: bất kỳ chuỗi nào có năm chữ cái, bắt đầu bằng a và kết thúc bằng s.

Biểu thức Chuỗi ví dụ Mô tả
^a...s$ abs  Không phù hợp vì chỉ có 3 ký tự
alias  Phù hợp
abyss  Phù hợp
Alias  Không phù hợp vì chữ cái đầu viết hoa A
An abacus  Không phù hợp vì chữ cái đầu viết hoa A và nhiều hơn 5 ký tự

Regular Expression trong Python được thể hiện qua module re, nên việc đầu tiên khi các bạn muốn sử dụng Regular Expression thì cần phải import module re vào chương trình. Thử với ví dụ trên:

import re
 
 pattern = '^a...s$'
 test_string = 'abyss'
 result = re.match(pattern, test_string)
 
 if result:
   print("Tim kiem thanh cong.")
 else:
   print("Tim kiem khong thanh cong.")

Ở đây chúng ta vừa sử dụng hàm re.match() để tìm kiếm test_string tương ứng với pattern. Phương thức trả về đối tượng tương ứng nếu tìm kiếm thành công, trả về None nếu không tìm thấy.  

Hầu như ngôn ngữ nào cũng hỗ trợ Regular Expression, kể đến như JavaScript, C#, Java, PHP, Ruby, SQL, Oracle, Perl... nhưng được sử dụng phổ biến nhất trong Unix/Linux.

Còn một số hàm khác có trong mudule re để hoạt động với RegEx. Trước khi đi sâu vào các hàm này, hãy tìm hiểu kĩ hơn về biểu thức chính quy RegEx.

Cú pháp pattern sử dụng trong RegEx Python

Pattern ta hiểu là một đối tượng mẫu, một phiên bản đã được biên dịch của một biểu thức chính quy. Để chỉ định biểu thức chính quy, ta sử dụng các ký tự đặc biệt, bao gồm:

[] . ^ $ * + ? {} () \ |

Trong ví dụ trên là ký tự ^$.

Dấu ngoặc vuông []

Dấu ngoặc vuông sử dụng để thể hiện tập các ký tự bạn muốn khớp.

Biểu thức Chuỗi ví dụ Mô tả
[abc] a  Khớp với ký tự a
ac  Khớp với ký tự a hoặc c
Hey Jude  Không khớp

Ở đây, [abc] sẽ khớp nếu chuỗi bạn truyền có chứa bất kỳ ký tự a, b hoặc c nào.

Bạn cũng có thể chỉ định một phạm vi các ký tự bằng cách sử dụng - bên trong dấu ngoặc vuông.

  • [a-e] tương tự với [abcde].
  • [1-4] tương tự với [1234].
  • [0-39] tương tự với [01239].

Nếu ký tự đầu tiên của tập hợp là ^ thì tất cả các ký tự không được định nghĩa trong tập hợp sẽ được so khớp.

  • [^abc] nghĩa là khớp với các chuỗi không có ký tự a, b hay c.
  • [^0-9] nghĩa là khớp với các chuỗi không có ký tự chữ số nào.

Các ký tự đặc biệt trong [] sẽ được coi như ký tự thông thường.

  • [(+)] khớp với bất kỳ chuỗi nào có ký tự (, + hoặc ).

Dấu chấm .

Dấu chấm khớp với bất kỳ ký tự đơn thông thường nào ngoại trừ ký tự tạo dòng mới '\n'.

Biểu thức Chuỗi ví dụ Mô tả
.. a  Không khớp vì chỉ có một ký tự
ac  Khớp vì có hai ký tự
acd  Khớp vì có hai ký tự trở lên

Dấu mũ ^

Biểu tượng dấu mũ ^ được sử dụng để khớp ký tự đứng đầu một chuỗi.

Biểu thức Chuỗi ví dụ Mô tả
^a a  Khớp vì bắt đầu bằng a
abc  Khớp vì bắt đầu bằng a
bac  Không khớp vì a không nằm ở đầu tiên
^ab abc  Khớp vì bắt đầu bằng ab
acb  Không khớp, bắt đầu bằng a nhưng ký tự tiếp theo không phải b

Biểu tượng Dollar $

Biểu tượng Dollar $ được sử dụng để khớp ký tự kết thúc một chuỗi.

Biểu thức Chuỗi ví dụ Mô tả
a$ a  Khớp vì kết thúc bằng a
formula  Khớp vì kết thúc bằng a
cab  Không khớp vì a không nằm ở vị trí cuối cùng

Dấu hoa thị *

Biểu tượng dấu hoa thị * có thể khớp với chuỗi có hoặc không có ký tự được định nghĩa trước nó. Ký tự này có thể được lặp lại nhiều lần mà không bị giới hạn số lượng.

Biểu thức Chuỗi ví dụ Mô tả
ma*n mn  Khớp vì ký tự trước * có thể không xuất hiện
man  Khớp vì có xuất hiện đầy đủ các ký tự
maaaan  Khớp vì ký tự trước * có thể xuất hiện nhiều lần
main  Không khớp vì không giống pattern, n không nằm kế a
woman  Khớp vì có xuất hiện đầy đủ các ký tự

Dấu cộng +

Biểu tượng dấu cộng + có thể khớp với chuỗi có một hoặc nhiều ký tự được định nghĩa trước nó. Ký tự này có thể được lặp lại nhiều lần mà không bị giới hạn số lượng.

Biểu thức Chuỗi ví dụ Mô tả
ma+n mn  Không khớp vì ký tự a trước + không xuất hiện
man  Khớp vì có xuất hiện đầy đủ các ký tự
maaaan  Khớp vì ký tự trước + có thể xuất hiện nhiều lần
main  Không khớp vì không giống pattern, n không nằm kế a
woman  Khớp vì có xuất hiện đầy đủ các ký tự

Dấu chấm hỏi ?

Biểu tượng dấu chấm hỏi có thể khớp với chuỗi có hoặc không có ký tự được định nghĩa trước nó. Ký tự này không thể được lặp lại nhiều lần, chỉ giới hạn số lượng với một lần xuất hiện.

Biểu thức Chuỗi ví dụ Mô tả
ma?n mn  Khớp vì ký tự trước ? có thể không xuất hiện
man  Khớp vì có xuất hiện đầy đủ các ký tự
maaaan  Không khớp vì ký tự trước ? chỉ có thể xuất hiện 1 lần
main  Không khớp vì không giống pattern, n không nằm kế a
woman  Khớp vì có xuất hiện đầy đủ các ký tự

Dấu ngoặc nhọn {}

Dấu ngoặc nhọn sử dụng theo công thức tổng quát: {n,m}, đại diện cho việc ký tự đằng trước nó có thể xuất hiện tối thiểu n lần vào tối đa m lần. nm là số nguyên dương và n <= m.

  • Nếu bỏ trống n, giá trị này mặc định bằng 0.
  • Nếu bỏ trống m, giá trị này mặc định là vô hạn.
Biểu thức Chuỗi ví dụ Mô tả
a{2,3} abc dat  Không khớp vì không thỏa mãn điều kiện
abc daat  Khớp vì có xuất hiện 2 ký tự a (daat)
aabc daaat  Khớp vì có xuất hiện 2 và 3 ký tự a (aabcdaaat)
aabc daaaat  Khớp vì có xuất hiện 2 và 3 ký tự a (aabcdaaaat)

Hãy thử một ví dụ nữa: RegEx [0-9] {2, 4} này khớp với chuỗi có tối thiểu 2 chữ số và tối đa không quá 4 chữ số.

Biểu thức Chuỗi ví dụ Mô tả
[0-9]{2,4} ab123csde  Khớp vì thỏa mãn điều kiện: ab123csde
12 and 345673  Khớp vì thỏa mãn điều kiện: 12345673
1 and 2  Không khớp vì chuỗi chỉ có 1 chữ số

Dấu sổ dọc |

Biểu tượng dấu sổ dọc | này có thể khớp với chuỗi tồn tại 1 trong 2 ký tự được định nghĩa trước và sau nó.

Biểu thức Chuỗi ví dụ Mô tả
a|b cde  Không khớp vì a, b đều không xuất hiện
ade  Khớp vì thỏa mãn điều kiện, có a xuất hiện: ade
acdbea  Khớp vì thỏa mãn điều kiện, ab đều xuất hiện: acdbea

Ở đây, a|b khớp với bất kỳ chuỗi nào chứa a hoặc b.

Dấu ngoặc đơn ()

Dấu ngoặc đơn () được sử dụng để gom nhóm các pattern lại với nhau, chuỗi sẽ khớp với biểu thức chính quy bên trong dấu ngoặc này.

Ví dụ: (a|b|c)xz khớp với bất kỳ chuỗi nào có a hoặc b hoặc c đứng trước xz.

Biểu thức Chuỗi ví dụ Mô tả
(a|b|c)xz ab xz  Không khớp vì a hay b có đứng trước nhưng không liền với xz
abxz  Khớp vì thỏa mãn điều kiện, có b xuất hiện sát trước xz: abxz
axz cabxz  Khớp vì thỏa mãn điều kiện, cả ab đều xuất hiện sát trước xz: axz cabxz

Dấu gạch chéo ngược \

Dấu gạch chéo ngược được sử dụng để thoát các ký tự đặc biệt, nghĩa là khi đứng trước một kí tự đặc biệt, \ sẽ biến kí tự này thành một kí tự thường, bạn có thể tìm kiếm kí tự đặc biệt này trong chuỗi như các kí tự thường khác.

Ví dụ: \$a sẽ khớp với chuỗi chứa ký tự $ đứng trước a. Ở đây, biểu tượng Dollar $ không sử dụng để khớp một chuỗi kết thúc bằng ký tự đi cùng nó như trong công cụ RegEx, $ chỉ là ký tự bình thường.

Tuy nhiên, một dấu gạch chéo ngược cũng sẽ biến một kí tự thường liền kế phía sau thành một kí tự đặc biệt.

Ví dụ, trường hợp ký tự b không có dấu gạch chéo ngược sẽ khớp với các ký tự b in thường, nhưng khi nó có thêm dấu gạch chéo ngược, \b thì nó trở thành kí tự đặc biệt, không khớp với bất kì ký tự nào nữa.

Một số pattern đi với \

1. \A - Khớp với các ký tự theo sau nó nằm ở đầu chuỗi.

Biểu thức Chuỗi ví dụ Mô tả
\Athe the sun  Khớp vì the nằm ở đầu chuỗi
In the sun  Không khớp vì the không nằm ở đầu chuỗi

2. \b - Khớp với các ký tự được chỉ định nằm ở đầu hoặc cuối của từ.

Biểu thức Chuỗi ví dụ Mô tả
\bfoo football  Khớp vì thỏa mãn điều kiện, foo nằm ở đầu chuỗi
a football  Khớp vì thỏa mãn điều kiện, foo nằm ở đầu từ thứ 2 trong chuỗi
afootball  Không khớp vì foo nằm ở giữa từ trong chuỗi.
foo\b the foo  Khớp vì thỏa mãn điều kiện, foo nằm ở cuối chuỗi
the afoo test  Khớp vì thỏa mãn điều kiện, foo nằm ở cuối từ thứ 2 trong chuỗi
the afootest  Không khớp vì foo nằm ở giữa từ trong chuỗi.

3. \B - Trái ngược với \b, khớp với các ký tự được chỉ định không nằm ở đầu hoặc cuối của từ.

Biểu thức Chuỗi ví dụ Mô tả
\bfoo football  Không khớp vì foo nằm ở đầu chuỗi
a football  Không khớp vì foo nằm ở đầu từ thứ 2 trong chuỗi
afootball  Khớp vì foo nằm ở giữa từ trong chuỗi.
foo\b the foo  Không khớp vì foo nằm ở cuối chuỗi
the afoo test  Không khớp vì foo nằm ở cuối từ thứ 2 trong chuỗi
the afootest Khớp vì foo nằm ở giữa từ trong chuỗi.

4. \d - Khớp với các ký tự là chữ số, tương đương với [0-9].

Biểu thức Chuỗi ví dụ Mô tả
\d 12abc3  Khớp vì thỏa mãn điều kiện: 12abc3
Python  Không khớp vì không có số nguyên nào xuất hiện

5. \D - Khớp với các ký tự không phải số, tương đương với [^0-9].

Biểu thức Chuỗi ví dụ Mô tả
\D 1ab34"50  Khớp vì thỏa mãn điều kiện: 1ab34"50
1345  Không khớp vì chuỗi toàn số nguyên xuất hiện

6. \s - Khớp với bất kỳ ký tự khoảng trắng nào, tương đương với [ \t\n\r\f\v].

Biểu thức Chuỗi ví dụ Mô tả
\s Python RegEx  Khớp vì chuỗi có khoảng trắng
PythonRegEx  Không khớp vì chuỗi không có khoảng trắng

7. \S - Khớp với bất kỳ ký tự nào không phải khoảng trắng, tương đương với [^ \t\n\r\f\v].

Biểu thức Chuỗi ví dụ Mô tả
\S a b  Khớp vì chuỗi có ký tự a b
       Không khớp vì chuỗi toàn bộ là khoảng trắng

8. \w - Khớp với bất kỳ ký tự chữ cái và chữ số nào, tương đương với [a-zA-Z0-9_].

Lưu ý: Dấu gạch dưới _ cũng được coi là một ký tự chữ cái và chữ số.

Biểu thức Chuỗi ví dụ Mô tả
\w 12&": ;c  Khớp vì chuỗi có ký tự chữ và số 12&": ;c
%"> !  Không khớp vì chuỗi không có ký tự chữ và số

9. \W - Khớp với bất kỳ ký tự nào không phải là chữ cái và chữ số, tương đương với [^a-zA-Z0-9_].

Biểu thức Chuỗi ví dụ Mô tả
\w 1a2%c  Khớp vì chuỗi có ký tự không phải chữ và số 1a2%c
Python  Không khớp vì chuỗi chỉ có ký tự chữ cái

Lưu ý: Dấu gạch dưới _ cũng được coi là một ký tự chữ cái và chữ số.

Tips: Để xây dựng các biểu thức chính quy RegEx, bạn có thể sử dụng công cụ kiểm tra RegEx như regex101. Công cụ này không chỉ tạo các biểu thức chính quy mà còn giúp bạn tìm hiểu nó kỹ hơn.

Bây giờ thì bạn đã hiểu những điều cơ bản về RegEx, hãy cùng thảo luận về cách sử dụng RegEx trong code Python.

Regular Expression trong Python

Regular Expression trong Python được thể hiện qua module re, nên việc đầu tiên khi các bạn muốn sử dụng regular expression thì cần phải import module re vào chương trình.

import re

Module này có rất nhiều các phương thức, hàm và hằng để làm việc với RegEx. Quantrimang.com sẽ liệt kê một số hay được sử dụng kèm theo ví dụ để bạn dễ hình dung và nắm bắt.

re.findall()

Phương thức re.findall() trả về một danh sách các chuỗi chứa tất cả các kết quả khớp với pattern đưa ra.

Cú pháp:

findall(partern, string)

Trong đó:

  • pattern là RegEx.
  • string là chuỗi cần so khớp.

Ví dụ: Trích xuất các số từ chuỗi cho trước sau: "hello 12 hi 89. Howdy 34"

import re
 
 string = 'hello 12 hi 89. Howdy 34'
 pattern = '\d+'
 
 result = re.findall(pattern, string) 
 print(result)

Kết quả trả về:

['12', '89', '34']

re.split()

Phương thức re.split() dùng biểu thức chính quy để ngắt chuỗi thành các chuỗi con và trả về danh sách các chuỗi con này.

Cú pháp: 

re.split(pattern, string, maxsplit)

Trong đó: 

  • pattern là RegEx.
  • string là chuỗi cần so khớp.
  • maxsplit (số nguyên) là số chuỗi tối đa sẽ được ngắt. Nếu để trống thì Python sẽ so khớp và cắt tất cả các chuỗi đạt điều kiện.

Ví dụ: Ngắt tại vị trí có ký tự khoảng trắng:

import re
 
 string = 'The rain in Vietnam.'
 pattern = '\s'
 
 result = re.split(pattern, string) 
 print(result)
 

Kết quả trả về:

['The', 'rain', 'in', 'Vietnam.']

Ví dụ: Ngắt chuỗi ở ký tự khoảng trắng đầu tiên:

import re
 
 string = 'The rain in Vietnam.'
 pattern = '\s'
 
 result = re.split(pattern, string, 1) 
 print(result)
 

Kết quả: 

['The', 'rain in Vietnam.']

Nếu không tìm thấy pattern, re.split() trả về danh sách chứa chuỗi rỗng.

re.sub()

Đây là một trong những phương thức quan trọng nhất sử dụng với Regular Expression 

Re.sub() sẽ thay thế tất cả các kết quả khớp với pattern trong chuỗi bằng một nội dung khác được truyền vào và trả về chuỗi đã được sửa đổi.

Cú pháp:

re.sub(pattern, replace, string, count)

Trong đó:

  • pattern là RegEx.
  • replace là nội dung thay thế cho chuỗi kết quả khớp với pattern.
  • string là chuỗi cần so khớp.
  • count (số nguyên) là số lần thay thế. Nếu để trống thì Python sẽ coi giá trị này bằng 0, so khớp và thay thế tất cả các chuỗi đạt điều kiện.

Ví dụ: Code chương trình xóa tất cả các khoảng trắng

import re
 
 # chuỗi nhiều dòng
 string = 'abc 12\
 de 23 \n f45 6'
 
 # so khớp các ký tự khoảng trắng
 pattern = '\s+'
 
 # chuỗi rỗng
 replace = ''
 
 new_string = re.sub(pattern, replace, string) 
 print(new_string)

Kết quả trả về: 

abc12de23f456

Nếu không tìm thấy kết quả phù hợp với pattern, re.sub() sẽ trả về chuỗi rỗng.

Ví dụ: Code chương trình xóa 2 khoảng trắng đầu tiên

import re
 
 # chuỗi nhiều dòng
 string = 'abc 12\
 de 23 \n f45 6 \n quantrimang website'
 
 # so khớp các ký tự khoảng trắng
 pattern = '\s+'
 replace = ''
 
 new_string = re.sub(r'\s+', replace, string, 2) 
 print(new_string)
 

Output trả về:

abc12de23 
  f45 6 
  quantrimang website

re.subn()

Phương thức re.subn() sử dụng tương tự như re.sub() ở trên, nhưng kết quả trả về bao gồm một tuple chứa hai giá trị: chuỗi mới sau khi được thay thế và số lần thay thế đã thực hiện.

import re
 
 # chuỗi nhiều dòng
 string = 'abc 12\
 de 23 \n f45 6 \n quantrimang website'
 
 # so khớp các ký tự khoảng trắng
 pattern = '\s+'
 
 # chuỗi rỗng
 replace = ''
 
 new_string = re.subn(pattern, replace, string) 
 print(new_string)
 

Kết quả trả về:

('abc12de23f456quantrimangwebsite', 6)

re.search()

Phương thức re.search() sử dụng để tìm kiếm chuỗi phù hợp với pattern RegEx. Nếu tìm kiếm thành công, re.search() trả về đối tượng khớp, nếu không, nó trả về None.

Cú pháp:

search(pattern, string)

Trong đó:

  • pattern là RegEx.
  • string là chuỗi cần so khớp.
import re
 
 string = "Quantrimang.com la website ban co the hoc Python"
 
 # Kiem tra xem 'Quantrimang' co nam o dau chuoi khong
 match = re.search('\AQuantrimang', string)
 
 if match: # nếu tồn tại chuỗi khớp
   print("Tim thay 'Quantrimang' nam o dau chuoi") # in ra thong bao nay
 else:
   print("'Quantrimang' khong nam o dau chuoi") # khong thi in ra thong bao nay
 

Kết quả trả về: 

Tim thay 'Quantrimang' nam o dau chuoi

Ở ví dụ này, match chứa đối tượng phù hợp khớp với pattern.

Đối tượng match

Một số phương thức và thuộc tính thường được sử dụng với đối tượng match.

match.group()

Phương thức group() trả về những phần của chuỗi khớp với pattern.

import re
 
 string = '39801 356, 2102 1111'
 
 pattern = '(\d{3}) (\d{2})'
 
 match = re.search(pattern, string)
 
 if match: #nếu tồn tại chuỗi khớp
   print(match.group()) # in ra kết quả
 else:
   print("Không khớp") # Không thì hiện thông báo
 
 # Output: 801 35

Ở đây, biến match chứa đối tượng match.

Ta có pattern là (\d{3}) (\d{2}) chia làm hai nhóm nhỏ (\d{3})(\d{2}). Bạn có thể nhận được một phần của chuỗi tương ứng với các nhóm con trong ngoặc đơn này như sau:

>>> match.group(1)
 '801'
 
 >>> match.group(2)
 '35'
 
 >>> match.group(1, 2)
 ('801', '35')
 
 >>> match.groups()
 ('801', '35')

match.start(), match.end() và match.span()

Hàm start() trả về chỉ mục bắt đầu của chuỗi con phù hợp. Tương tự, end() trả về chỉ mục kết thúc của chuỗi con phù hợp.

>>> match.start()
 2
 >>> match.end()
 8

Hàm span() trả về tuple chứa chỉ mục bắt đầu và kết thúc của phần chuỗi phù hợp.

>>> match.span()
 (2, 8)

match.re và match.string

Thuộc tính re của đối tượng match sẽ trả về một biểu thức chính quy. Tương tự, thuộc tính string trả về chuỗi đã được truyền trong đoạn code.

>>> match.re
 re.compile('(\\d{3}) (\\d{2})')
 >>> match.string
 '39801 356, 2102 1111'

Trên đây là tất cả các phương thức thường được sử dụng nhất trong module re.

Sử dụng tiền tố r trước RegEx

Khi tiền tố r hoặc R được sử dụng trước một biểu thức chính quy đại diện cho việc chuỗi tiếp sau nó chỉ là những ký tự bình thường.

Ví dụ: '\n' là một dòng mới newline, còn r'\n' có nghĩa là chuỗi bao gồm hai ký tự: dấu gạch chéo ngược \n.

Dấu gạch chéo ngược \ được sử dụng để thoát các ký tự như đã nói ở trên. Tuy nhiên, sử dụng tiền tố r trước \ thì nó chỉ là một ký tự bình thường.

import re
 
 string = '\n and \r are escape sequences.'
 
 result = re.findall(r'[\n\r]', string) 
 print(result)
 
 # Output: ['\n', '\r']

Bài trước: Khai báo @property trong Python

Thứ Năm, 22/08/2019 08:28
58 👨 545