Trang chủ Kiến Thức Công Nghệ Tìm hiểu regex trong ngôn ngữ Dart qua ví dụ cơ bản
Công Nghệ

Tìm hiểu regex trong ngôn ngữ Dart qua ví dụ cơ bản

Chia sẻ
Tìm hiểu regex trong ngôn ngữ Dart qua ví dụ cơ bản
Chia sẻ

Trích xuất text bất kỳ từ một string cho trước bằng cách sử dụng regex trong Dart

Text extraction from an LRC lyrics file

Làm việc và xử lý text là một vấn đề lập trình phổ biến. Tuy nhiên, tôi thường cố gắng tránh sử dụng các regular expressions (regex) vì chúng hoàn toàn không thể đọc được và hơi khó hiểu, khó tiếp thu và khó nhớ:

Dart

RegExp _email = RegExp(r"^((([a-z]|d|[!#$%&'*+-/=?^_`{|}~]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])+(.([a-z]|d|[!#$%&'*+-/=?^_`{|}~]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])+)*)|((x22)((((x20|x09)*(x0dx0a))?(x20|x09)+)?(([x01-x08x0bx0cx0e-x1fx7f]|x21|[x23-x5b]|[x5d-x7e]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])|(\([x01-x09x0bx0cx0d-x7f]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF]))))*(((x20|x09)*(x0dx0a))?(x20|x09)+)?(x22)))@((([a-z]|d|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])|(([a-z]|d|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])([a-z]|d|-|.|_|~|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])*([a-z]|d|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF]))).)+(([a-z]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])|(([a-z]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])([a-z]|d|-|.|_|~|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])*([a-z]|[u00A0-uD7FFuF900-uFDCFuFDF0-uFFEF])))$");

Tuy nhiên, thỉnh thoảng, việc sử dụng chúng sẽ dễ dàng hơn so với việc bạn phải tìm ra cách để viết một lớp parser để lấy được đoạn text mà bạn mong muốn trong một String cho trước theo quy tắc nào đó.

1. Xác định vấn đề

Giả sử bạn đang cố gắng trích xuất thời gian và lời bài hát từ tệp text LRC:

Dart

[00:12.30]Twinkle, twinkle, little star 
[00:17.60]How I wonder what you are
[00:21.50]Up above the world so high 
[00:25.30]Like a diamond in the sky

Bạn có thể lấy từng dòng khá dễ dàng như thế này:

Dart

List<String> rawLines = text.split('n');
print(rawLines[0]); // [00:12.30]Twinkle, twinkle, little star

Nhưng điều bạn thực sự muốn làm là lấy thời gian và từ ngữ cho từng dòng, đại loại như sau:

Dart

final time = Duration(minutes: 0, seconds: 12, milliseconds: 300);
final words = 'Twinkle, twinkle little star';

Điều đó có nghĩa là có bốn nhóm riêng biệt để trích xuất:

Tôi biết có 1 một regex có thể giúp ta làm điều ta mong muốn ở trên. Tuy nhiên, nếu tôi đưa bạn luôn thì bạn không thể nào đọc hiểu được regex đó làm gì. Do đó, chúng ta hãy cùng nhau giải quyết vấn đề từng bước một.

2. Tìm hiểu Regex cơ bản

Để tạo một trình so sánh regex (regex matcher), bạn sử dụng class RegExp trong Dart.

Dart

final regex = RegExp(r'');

Bạn đặt regular expression bên trong dấu ngoặc kép. Sẽ rất hữu ích khi sử dụng một string thô (nghĩa là một string bắt đầu bằng r) để bạn không phải tránh quá nhiều thứ sau này.

2.1 Match the start and end of the line (Khớp đầu và cuối dòng)

Điều này không hoàn toàn cần thiết, nhưng nếu bạn vẫn đang làm việc với toàn bộ dòng, thì việc so khớp phần đầu và phần cuối của dòng có thể tránh một số bất ngờ mà bạn có thể gặp phải.

  • ^ khớp với đầu dòng
  • $ khớp với cuối dòng

Điều đó có nghĩa là regex của bạn sẽ trông như thế này:

Dart

final regex = RegExp(r'^$');

2.2 Match the constant parts (Khớp các phần không đổi)

Các ký tự sẽ không thay đổi trong string là [, :, ., và ]:

Nhưng  [, ., và ] đều có nghĩa đặc biệt trong regex, vì vậy bạn phải thoát khỏi chúng bằng cách thêm tiền tố :

  • [
  • .
  • ]

Điều đó làm cho regex hiện tại trông như thế này:

Dart

final regex = RegExp(r'^[:.]$');

2.3 Match the variable parts

Một lần nữa, bốn nhóm mà bạn muốn nắm bắt là phút(minutes), giây(seconds), giây phân số(fractional seconds) và các từ:

Bạn có thể sử dụng các pattern sau để kiểm tra xem các từ có match với điều kiện của chúng ta hay không:

  • [0-9]+ — Khớp một hoặc nhiều chữ số. Dấu ngoặc khớp với một trong bất kỳ mục nào trong phạm vi và + là ký tự đại diện có nghĩa là một hoặc nhiều kết quả phù hợp. (Ngoài ra, bạn có thể sử dụng d thay vì [0-9], nhưng tôi thấy [0-9] dễ nhớ hơn.)
  • .* — Khớp 0 hoặc nhiều ký tự. Các . khớp với bất kỳ ký tự đơn nào và * là ký tự đại diện để khớp với 0 hoặc nhiều lần xuất hiện của bất kỳ ký tự nào đứng trước nó. Chúng ta sẽ sử dụng điều này cho lời bài hát để cho phép vài bài hát có dòng trống trong khi vẫn chứa time stamp.

Điều đó làm cho regex trông như thế này:

Dart

final regex = RegExp(r'^[[0-9]+:[0-9]+.[0-9]+].*$');

Quá trình khớp thực sự đã hoàn thành, nhưng bạn không có cách nào để trích xuất các phần biến. Bạn phải sử dụng các nhóm để làm điều đó.

2.4 Capture the groups

Bạn có thể gộp các nhóm bằng cách bao quanh chúng bằng dấu ngoặc đơn:

Dart

final regex = RegExp(r'^[([0-9]+):([0-9]+).([0-9]+)](.*)$');

Bây giờ bạn đã sẵn sàng để trích xuất các phần bên trong dấu ngoặc đơn.

3. Pulling it all together (Kết hợp các điều trên lại)

Đây là cách bạn trích xuất text bạn muốn:

Dart

final line = '[00:12.30]Twinkle, twinkle little star';
final regex = RegExp(r'^[([0-9]+):([0-9]+).([0-9]+)](.*)$');
final match = regex.firstMatch(line);
final everything = match.group(0);  // [00:12.30]Twinkle, twinkle little star
final minutes = match.group(1);     // 00
final seconds = match.group(2);     // 12
final fraction = match.group(3);    // 30
final words = match.group(4);       // Twinkle, twinkle little star

Chú ý:

  • Bạn có thể kiểm tra matching hay không bằng cách gọi hàm firstMatch trong regex. Bạn hoàn toàn sử dụng được vởi vì bạn đã chắc chắn nó đã khớp với toàn bộ dòng. Nếu ban đầu bạn không chia toàn bộ text thành các dòng thì bạn có thể gọi hàmregex.allMatches, nó sẽ cung cấp cho bạn một collection các kết quả phù hợp.
  • Như bạn có thể thấy, group(0) khớp với mọi thứ, trong khi group(1) đến group(4) khớp với các phần mà bạn đã bao lại bằng dấu ngoặc đơn.

Các nhóm thời gian được trích xuất vẫn là string, vì vậy nếu bạn muốn chuyển đổi chúng thành Duration, thì bạn chỉ cần thực hiện như sau:

Dart

final time = Duration(
  minutes: int.parse(minutes),
  seconds: int.parse(seconds),
  milliseconds: int.parse(fraction.padLeft(3, '0')),
);

Đó là toàn bộ những gì có trong regex của ngôn ngữ Dart. Nếu bạn có thể vượt qua được độ khó đọc, khó hiểu của các regex matching pattern thì regex là một cách vô cùng thuận tiện để trích xuất những gì bạn cần từ các text.

Bài viết được dịch từ đây.

Bài viết cùng chuyên mục
Tối ưu ứng dụng với cấu trúc dữ liệu cơ bản và bitwise
Công Nghệ

Tối ưu ứng dụng với cấu trúc dữ liệu cơ bản và bitwise

Trong bài viết này, 200Lab sẽ chia sẻ những trường hợp dễ...

Công Nghệ

So sánh Flutter vs React Native: Framework nào đáng học năm 2021

Điểm chung của Flutter, React Native đều là Cross-platform Mobile, build native...

HTTP/2 là gì? So sánh HTTP/2 và HTTP/1
Công Nghệ

HTTP/2 là gì? So sánh HTTP/2 và HTTP/1

Từ khi Internet ra đời, sự phát triển về các giao thức...

Upload File từ Frontend đến Backend mà rất nhiều bạn vẫn đang làm sai!!
Công Nghệ

Upload File từ Frontend đến Backend mà rất nhiều bạn vẫn đang làm sai!!

1. Client encode file (base64) rồi gởi về backend 200Lab đã từng...

Công Nghệ

React Native – Hướng dẫn làm việc với Polyline và Animated-Polyline trên Map

Vẽ đường đi trên bản đồ là một nghiệp vụ vô cùng...

Công Nghệ

Hybrid App và Native App: Những khác biệt to lớn

Bất cứ khi nào một công ty quyết định làm ứng dụng...

Web/System Architecture 101 – Kiến trúc web/hệ thống cơ bản cho người mới
Công Nghệ

Web/System Architecture 101 – Kiến trúc web/hệ thống cơ bản cho người mới

Đây là một kiến trúc cơ bản mà bất kì một người...

Công Nghệ

Tư duy kiến trúc thông qua các trò chơi mà rất nhiều bạn không biết

Tư duy kiến trúc là gì? Tư duy kiến trúc có thể...