Regular Expression (RegEx) trong Python

Chương tiếp theo của bài học nâng cao Python, chúng ta sẽ cùng 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ứcChuỗi ví dụMô tả
^a...s$absKhông phù hợp vì chỉ có 3 ký tự
aliasPhù hợp
abyssPhù hợp
AliasKhông phù hợp vì chữ cái đầu viết hoa A
An abacusKhô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ự ^ và $.

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ứcChuỗi ví dụMô tả
[abc]aKhớp với ký tự a
acKhớp với ký tự a hoặc c
Hey JudeKhông khớp

Ở đây, [abc] sẽ khớp nếu chuỗi bạn truyền có chứa bất kỳ ký tự ab 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ự ab 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ứcChuỗi ví dụMô tả
..aKhông khớp vì chỉ có một ký tự
acKhớp vì có hai ký tự
acdKhớ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ứcChuỗi ví dụMô tả
^aaKhớp vì bắt đầu bằng a
abcKhớp vì bắt đầu bằng a
bacKhông khớp vì a không nằm ở đầu tiên
^ababcKhớp vì bắt đầu bằng ab
acbKhô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ứcChuỗi ví dụMô tả
a$aKhớp vì kết thúc bằng a
formulaKhớp vì kết thúc bằng a
cabKhô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ứcChuỗi ví dụMô tả
ma*nmnKhớp vì ký tự trước * có thể không xuất hiện
manKhớp vì có xuất hiện đầy đủ các ký tự
maaaanKhớp vì ký tự trước * có thể xuất hiện nhiều lần
mainKhông khớp vì không giống pattern, n không nằm kế a
womanKhớ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ứcChuỗi ví dụMô tả
ma+nmnKhông khớp vì ký tự a trước + không xuất hiện
manKhớp vì có xuất hiện đầy đủ các ký tự
maaaanKhớp vì ký tự trước + có thể xuất hiện nhiều lần
mainKhông khớp vì không giống pattern, n không nằm kế a
womanKhớ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ứcChuỗi ví dụMô tả
ma?nmnKhớp vì ký tự trước ? có thể không xuất hiện
manKhớp vì có xuất hiện đầy đủ các ký tự
maaaanKhông khớp vì ký tự trước ? chỉ có thể xuất hiện 1 lần
mainKhông khớp vì không giống pattern, n không nằm kế a
womanKhớ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. n và m 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ứcChuỗi ví dụMô tả
a{2,3}abc datKhông khớp vì không thỏa mãn điều kiện
abc daatKhớp vì có xuất hiện 2 ký tự a (daat)
aabc daaatKhớp vì có xuất hiện 2 và 3 ký tự a (aabc và daaat)
aabc daaaatKhớp vì có xuất hiện 2 và 3 ký tự a (aabc và daaaat)

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ứcChuỗi ví dụMô tả
[0-9]{2,4}ab123csdeKhớp vì thỏa mãn điều kiện: ab123csde
12 and 345673Khớp vì thỏa mãn điều kiện: 12 và 345673
1 and 2Khô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ứcChuỗi ví dụMô tả
a|bcdeKhông khớp vì ab đều không xuất hiện
adeKhớp vì thỏa mãn điều kiện, có a xuất hiện: ade
acdbeaKhớp vì thỏa mãn điều kiện, a và b đề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ứcChuỗi ví dụMô tả
(a|b|c)xzab xzKhông khớp vì a hay b có đứng trước nhưng không liền với xz
abxzKhớp vì thỏa mãn điều kiện, có b xuất hiện sát trước xzabxz
axz cabxzKhớp vì thỏa mãn điều kiện, cả a và b đều xuất hiện sát trước xzaxz 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ứcChuỗi ví dụMô tả
\Athethe sunKhớp vì the nằm ở đầu chuỗi
In the sunKhô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ứcChuỗi ví dụMô tả
\bfoofootballKhớp vì thỏa mãn điều kiện, foo nằm ở đầu chuỗi
a footballKhớp vì thỏa mãn điều kiện, foo nằm ở đầu từ thứ 2 trong chuỗi
afootballKhông khớp vì foo nằm ở giữa từ trong chuỗi.
foo\bthe fooKhớp vì thỏa mãn điều kiện, foo nằm ở cuối chuỗi
the afoo testKhớp vì thỏa mãn điều kiện, foo nằm ở cuối từ thứ 2 trong chuỗi
the afootestKhô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ứcChuỗi ví dụMô tả
\bfoofootballKhông khớp vì foo nằm ở đầu chuỗi
a footballKhông khớp vì foo nằm ở đầu từ thứ 2 trong chuỗi
afootballKhớp vì foo nằm ở giữa từ trong chuỗi.
foo\bthe fooKhông khớp vì foo nằm ở cuối chuỗi
the afoo testKhông khớp vì foo nằm ở cuối từ thứ 2 trong chuỗi
the afootestKhớ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ứcChuỗi ví dụMô tả
\d12abc3Khớp vì thỏa mãn điều kiện: 12abc3
PythonKhô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ứcChuỗi ví dụMô tả
\D1ab34"50Khớp vì thỏa mãn điều kiện: 1ab34"50
1345Khô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ứcChuỗi ví dụMô tả
\sPython RegExKhớp vì chuỗi có khoảng trắng
PythonRegExKhô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ứcChuỗi ví dụMô tả
\Sa bKhớ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ứcChuỗi ví dụMô tả
\w12&": ;cKhớ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ứcChuỗi ví dụMô tả
\w1a2%cKhớp vì chuỗi có ký tự không phải chữ và số 1a2%c
PythonKhô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}) và (\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 \ và 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']

Leave a Reply