, October 02, 2022

0 kết quả được tìm thấy

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

  • Đăng bởi  Kieu Hoa
  •  Aug 31, 2021

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

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ớ:

RegExp _email = RegExp(r"^((([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+(\.([a-z]|\d|[!#\$%&'\*\+\-\/=\?\^_`{\|}~]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])+)*)|((\x22)((((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(([\x01-\x08\x0b\x0c\x0e-\x1f\x7f]|\x21|[\x23-\x5b]|[\x5d-\x7e]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(\\([\x01-\x09\x0b\x0c\x0d-\x7f]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]))))*(((\x20|\x09)*(\x0d\x0a))?(\x20|\x09)+)?(\x22)))@((([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|\d|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])))\.)+(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])|(([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])([a-z]|\d|-|\.|_|~|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF])*([a-z]|[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\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:

[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:

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:

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.

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:

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:

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:

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:

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:

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:

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 liên quan

Flutter Tutorial 2022: Giới thiệu Flutter Travel App

Trong series này, chúng ta sẽ cùng nhau thực hiện ứng dụng Travel App với một bản UI Design vô cùng đẹp mắt, thu hút có sẵn....

Flutter Tutorial 2022: Giới thiệu Flutter Travel App
So sánh StatelessWidget và StatefulWidget

Trong bài viết này, các bạn sẽ có cái nhìn tổng quát về hai loại widget lớn nhất là StatelessWidget và StatefulWidget....

So sánh StatelessWidget và StatefulWidget
Tổng hợp các Shortcuts, Extensions & Settings trong VSCode khi lập trình Flutter

Trong bài viết này, mình sẽ liệt kê cho bạn các shortcuts, extensions, setting mà bạn nên sử dụng để lập trình Flutter hàng ngày....

Tổng hợp các Shortcuts, Extensions & Settings trong VSCode khi lập trình Flutter
[Phần 1] Những extension cần thiết khi làm việc với Flutter trên VS Code

VS Code là 1 text editor tuyệt vời được phát triển bởi Mircosoft. Đây là một trong các công cụ được developer trên toàn thế giới yêu thích vì sự tiện lợi, dễ dùng và nó chứa hàng ngàn, thậm chí trăm ngàn các extension phục vụ cho bạn trong quá trình bạn phát triển phần mềm....

[Phần 1] Những extension cần thiết khi làm việc với Flutter trên VS Code
Fix lỗi Flutter 3 không thể build app trên iOS

Cách fix lỗi Flutter 3 không thể build và run được app trên iOS với video hướng dẫn chia tiết...

Fix lỗi Flutter 3 không thể build app trên iOS
You've successfully subscribed to 200Lab Blog
Great! Next, complete checkout for full access to 200Lab Blog
Xin chào mừng bạn đã quay trở lại
OK! Tài khoản của bạn đã kích hoạt thành công.
Success! Your billing info is updated.
Billing info update failed.
Your link has expired.