VBScript.RegExp sẽ bị xóa khỏi Windows: thư viện Pcre2 giải pháp thay thế tối ưu (1 người xem)

Người dùng đang xem chủ đề này

HeSanbi

Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
Tham gia
24/2/13
Bài viết
2,807
Được thích
4,529
Giới tính
Nam
Do microsoft đã dần loại bỏ thư viện Scripting khỏi Windows trong các phiên bản mới nhất, làm khó khăn hơn cho các bạn muốn lập trình trong VBA.
Giờ đây các bạn muốn tận dụng thư viện này để khởi tạo lớp Biểu thức chính quy VBScript.RegExp thì sẽ gặp thông báo lỗi, thư viện không hoạt động trong Runtime hoặc thông báo lỗi không thể chạy.


Thư viện Pcre2 giải pháp thay thế tối ưu, với mã nguồn được viết với ngôn ngữ C, tận dụng đa luồng để xử lý chuỗi, giải thuật tối ưu do được nhiều lập trình viên cộng đồng đóng góp, tốc độ tối ưu, hỗ trợ nhiều cú pháp mới nhất dành cho biểu thức chính quy.
Tuy nhiên khi lập trình với thư viện ngoài cần có các tệp DLL, và nhúng Dll vào các dự án Office. Đòi hỏi các hiểu biết về lập trình Win API.
Hôm nay dựa vào Repo VBPcre2 trên Github tôi viết lại bản tương thích cả 32 và 64 bit để có thể hoạt động trên nhiều nền tảng nhiều phiên bản Office.
Nếu các bạn đang viết mã trên IDE mới là TwinBasic, cũng có thể tận dụng lại mã nguồn từ tệp bên dưới.

Với việc tận dụng thư viện Pcre2, PCRE2 hỗ trợ nhiều cú pháp hiện đại, bao gồm nhóm bất đối xứng ((?>...)), tùy chọn lớp ký tự (ví dụ: (?i)), tên thay thế thuộc tính Unicode (\p{...}), tùy chọn lồng nhau ((?i:...)), ký tự đại diện cho khoảng trắng (\s) và các kiểu hậu tố thời gian (\d{1,2}).
Dưới đây là một số cú pháp hiện đại phổ biến trong PCRE2:

  • Nhóm bất đối xứng (Possessive Groups): (?>...) nhóm các biểu thức con lại với nhau mà không cho phép backtracks, giúp cải thiện hiệu suất.
  • Tùy chọn lớp ký tự (Character class options): Sử dụng (?i) để bật chế độ không phân biệt chữ hoa chữ thường trong biểu thức con đó, (?m) cho chế độ đa dòng, và (?s) cho chế độ xem mọi ký tự là một phần của một dòng.
  • Tên thay thế thuộc tính Unicode: Sử dụng \p{<name>} hoặc \P{<name>} để khớp với các ký tự có thuộc tính Unicode cụ thể (ví dụ: \p{Lu} cho chữ hoa).
  • Tùy chọn lồng nhau (Nested Options): (?i:abc) áp dụng tùy chọn không phân biệt chữ hoa chữ thường chỉ cho nhóm con đó.
  • Ký tự đại diện cho khoảng trắng và từ: \s khớp với mọi ký tự khoảng trắng (bao gồm cả các ký tự không phải ASCII), và \w khớp với mọi ký tự từ.
  • Kiểu hậu tố thời gian (Time-based quantifiers): Cho phép bạn định lượng một mẫu trong một khoảng thời gian nhất định.
  • Phân tách tham chiếu ngược (Backreference): \1 tham chiếu đến nội dung của nhóm bắt số 1.
    • Positive Lookahead: (?=...) khớp khi mẫu theo sau là mẫu trong ngoặc.
    • Negative Lookahead: (?!...) khớp khi mẫu theo sau không phải là mẫu trong ngoặc.
    • Positive Lookbehind: (?<=...) khớp khi mẫu đứng trước là mẫu trong ngoặc.
    • Negative Lookbehind: (?<!...) khớp khi mẫu đứng trước không phải là mẫu trong ngoặc.
  • Đệ quy không tên ((?R)): Khi bạn sử dụng (?R) trong một mẫu, nó sẽ gọi lại chính mẫu đó từ đầu. Điều này hữu ích cho các cấu trúc có thể lồng nhau vô hạn, như dấu ngoặc đơn hoặc các cấu trúc cây.
  • Đệ quy có tên ((?&name)):Nếu bạn đã đặt tên cho một nhóm lặp lại bằng cách sử dụng (?<name>...), bạn có thể gọi lại nhóm đó bằng cách sử dụng (?&name).

Với tệp Dll có hai dạng được Export từ mã nguồn cho hai cách gọi là __stdcall và __cdeclcall
Với mã nguồn bên dưới, có thể tận dụng cả hai cách đóng gói, tùy theo cách bạn muốn sử dụng.

Với cú pháp của thư viện có một chút khác so với thư viện cũ, tất cả có trong các lớp trong tệp, các bạn có thể sửa lại mã nguồn để đạt được mong muốn tái sử dụng lại cú pháp của thư viện cũ.

Mã trong tệp được cải tiến sang tương thích 64 bit dựa vào mã nguồn VBPcre2 chỉ hỗ trợ 32bit dành cho VB6 tại:

Dưới đây là tệp phiên bản đầu tiên, sẽ có các bản cập nhật mới trong tương lai

=============================================================
***Cập nhật: sửa lỗi và thêm truy vấn name grouping, với hàm IndexByName trong lớp IRegExp

Ví dụ:
JavaScript:
Sub test1()
  Dim re As IRegExp, m As Object
  Set re = New IRegExp
  re.Pattern = "(?<numbers>123).+?(?<chars>abc).+?(?<sign>@@@)"
  re.GlobalSearch = True
  Set m = re.Execute("123  abc  @@@")
  Debug.Print "          Match count: "; m.count
  Debug.Print "           Match Text: "; m(0)
  Debug.Print "   Match - FirstIndex: "; m(0).FirstIndex
  Debug.Print "    Match - LastIndex: "; m(0).LastIndex
  Debug.Print "          SubMatch[1]: "; m(0).SubMatches(1)
  Debug.Print "SubMatch - FirstIndex: "; m(0).SubMatchFirstIndex(1)
  Debug.Print " SubMatch - LastIndex: "; m(0).SubMatchLastIndex(1)
  Debug.Print "  SubMatch['numbers']: "; m(0).SubMatches(re.IndexByName("numbers"))
End Sub

Nếu các bạn đã quen với cách thiết lặp trong RegExp là Global, MultiLine, hãy sửa thành GlobalSearch, Options.Compile.MultiLine

=============================================================
Nếu các bạn đang sử dụng Excel 365, hãy thử hai đoạn mã dưới đây để kiểm tra lỗi trong VBA.

Lỗi Runtime thông báo lỗi khi sử dụng thư viện cũ:

1758093802105.png


Đoạn mã này sử dụng RegExp đã được Microsoft nhúng trực tiếp vào trong VBA sẽ chạy lỗi trên Office 365:
(Khi chạy thử đoạn mã này gây Crash ứng dụng Excel)

JavaScript:
Sub VBA_365_RegExp_test()
  ' RegExp in Office 365
  With New RegExp
    .Global = True
    .IgnoreCase = True
    .MultiLine = True
    .pattern = "(.)(?=.*\1)"
    Debug.Print .test("ss")
  End With
End Sub

Đoạn mã này sử dụng VBScript.RegExp đã được Microsoft cập nhật chạy lỗi:
JavaScript:
Sub VBA_VBScript_Regex_test()
  With Interaction.CreateObject("VBScript.RegExp")
    .Global = True
    .IgnoreCase = True
    .MultiLine = True
    .pattern = "(.)(?=.*\1)"
    Debug.Print .test("ss")
  End With
End Sub
 

File đính kèm

Lần chỉnh sửa cuối:
Mới vừa đăng bài thì ChatGPT đã cập nhật thông tin cho Copilot, vô tình hỏi thông tin về Pcre2 thì nó gợi ý chính bài viết này


1758099830596.png
 
Upvote 0
***Cập nhật: sửa lỗi và thêm truy vấn name grouping, với hàm IndexByName trong lớp IRegExp

Ví dụ:
JavaScript:
Sub test1()
  Dim re As IRegExp, m As Object
  Set re = New IRegExp
  re.Pattern = "(?<numbers>123).+?(?<chars>abc).+?(?<sign>@@@)"
  re.GlobalSearch = True
  Set m = re.Execute("123  abc  @@@")
  Debug.Print "          Match count: "; m.count
  Debug.Print "           Match Text: "; m(0)
  Debug.Print "   Match - FirstIndex: "; m(0).FirstIndex
  Debug.Print "    Match - LastIndex: "; m(0).LastIndex
  Debug.Print "          SubMatch[1]: "; m(0).SubMatches(1)
  Debug.Print "SubMatch - FirstIndex: "; m(0).SubMatchFirstIndex(1)
  Debug.Print " SubMatch - LastIndex: "; m(0).SubMatchLastIndex(1)
  Debug.Print "  SubMatch['numbers']: "; m(0).SubMatches(re.IndexByName("numbers"))
End Sub
 
Upvote 0

Bài viết mới nhất

Back
Top Bottom