Nhờ chỉnh code loại trùng text dùng REGEXP

Liên hệ QC

eke_rula

Thành viên tích cực
Tham gia
12/11/16
Bài viết
1,076
Được thích
1,245
Em có đoạn code:
PHP:
Sub tachtrung2()
    Dim i As Long, j As Long, text As String, text2 As String, text3 As String
    text3 = "Dien;Dien;Dan;dien;giai;phap;dien;dan;phap;excel;Excel;phap;dan;2017;2017;dien;EXCEL;2017"
    text = Replace(";" & text3 & ";", ";", "   ")
    With CreateObject("vbscript.regexp")
        .Global = True
        .ignorecase = True
        .Pattern = "((\s\w+\s).+)\2"
        Do While .test(text)
            text = .Replace(text, "$1 ")
            i = 0
            For Each subl In .Execute(text)
                If InStr(text2, Trim(.Execute(text).Item(i).submatches(1))) = 0 Then
                    text2 = text2 & " | " & Trim(.Execute(text).Item(i).submatches(1))
                End If
                i = i + 1
            Next
        Loop
    End With
    MsgBox ("- " & Application.Trim(text) + ChrW(10) & "- " & text2)
End Sub
Kết quả của đoạn code trên tạo ra là chuỗi sau khi loại trùng hết (text) và liệt kê các chuỗi text bị trùng (text2)
- Sau khi chay code được text2, nhưng đoạn text2 lúc nào cũng có dấu "|" ở đầu và cuối, em có thể dùng Application.SUBSTITUTE để loại dấu "|" đầu và cuối, nhưng trong VBA có hàm replace , em đã thử replace(text,text2,"",,1) nhưng không được, cho em hỏi là em có thể dùng replace trong trường hợp này được không?
- Kết quả text2="| Dien | dan | 2017 | phap | Dan | excel" , bị trùng chữ "dan" và "Dan", em đã dùng Instr để xác định xem xuất hiện trong chuỗi không nhưng sao kết quả text2 vẫn có trùng, nhờ các anh/chị xem và chỉnh code dùm em.
Em cám ơn!!
 
Lần chỉnh sửa cuối:
\s là 1 ký tự của tập [ \f\n\r\t\v]
\b không là ký tự, chỉ là "vị trí" giữa ký tự thuộc [a-zA-Z0-9_]) và ký tự [^A-Za-z0-9_]. Cũng có nghĩa là vị trí ^ hoặc $ nếu ký tự đầu tiên hoặc cuối cùng trong chuỗi thuộc [A-Za-z0-9_]
Thế bạn hiểu ^ và $ thế nào? Chúng cũng không là ký tự gì cả mà là "vị trí" đầu và cuối chuỗi (cả đầu và cuối mỗi dòng nếu MultiLine = TRUE). Thì \b nó cũng thế, cũng chỉ là "vị trí" thôi chứ có là ký tự gì đâu?

Vài vd. cho dễ hiểu. Tôi có chuỗi text = "Mai17 Hoa21 Nga39 Hanh14"

1. Thế bạn cho ^ nó là ký tự nào? Là "M"? Làm gì có chuyện đó. Thế $ nó là ký tự nào? Là "4"? Làm gì có chuyện đó.

"^" là "vị trí" đầu chuỗi. Hay nói nôm na thì "trước ký tự đầu tiên của chuỗi" có một vị trí vô hình, ta cứ tưởng tượng là chỗ mà ký tự đầu tiên "tiếp xúc" với "thế giới" bên ngoài. Và "chỗ đó" người ta gọi là "^". Về "$" cũng tương tự.

2. Nếu bạn vẫn chưa hiểu về \b thì hãy hiểu như sau:
"\b\w" có nghĩa là hoặc ký tự đại diện bởi \w là ký tự đầu tiên của chuỗi (của dòng) - lúc này \b trùng với ^, có cùng nghĩa với ^ - hoặc trước ký tự đó là ký tự thuộc [^A-Za-z0-9_]

"\w\b" có nghĩa là hoặc ký tự đại diện bởi \w là ký tự cuối cùng của chuỗi (của dòng) - lúc này \b trùng với $, có cùng nghĩa với $ - hoặc sau ký tự đó là ký tự thuộc [^A-Za-z0-9_]

3. Ta làm bài toán cực đơn giản. Xóa từ đầu tiên trong chuỗi. Tất nhiên có nhiều cách nhưng ta xét các cách cụ thể nhằm mục đích giải thích vài chuyện.
Nếu bạn có .Pattern = "\s\w+?\s" thì sẽ chỉ tìm thấy các từ thứ 2, 3. Bởi trước từ đầu tiên và sau từ cuối cùng không có ký tự nào là "dấu cách", TAB, vbCr, vbLf ... Chú ý: \s là ký tự thuộc [ \f\n\r\t\v].
Nếu có .Pattern = "^\w+?\s" thì tìm thấy và chỉ tìm thấy từ đầu tiên thôi. Bạn thấy rõ ràng trước "M" không có ký tự nào. Chỉ có "vị trí", "nơi "tiếp xúc" gọi là "^"

4. Bạn có text = "Mai tieu thu, Hong mat nau, My Ha noi choi than voi Hoa (Manh la ban cua ca hai)"
Bài toán: Tìm tất cả các tên có chữ cái đầu là "M"
Bạn không thể dùng "\s\M\w*?\s" được vì trước "Mai" không có ký tự nào, vì trước "Manh" là ký tự "(" không thuộc tập [ \f\n\r\t\v]. Tức bạn chỉ tìm thấy " My "
Nếu .Pattren = "\bM\w*?\b" thì bạn tìm thấy hết. Vì trước "Mai" là "^" cùng nghĩa với "\b", trước "My" là "dấu cách", vậy giữa "dấu cách" (thuộc tập [^A-Za-z0-9_]) và "M" (thuộc tập [A-Za-z0-9_]) có "\b", trước "Manh" là ký tự "(", vậy giữa ký tự "(" (thuộc tập [^A-Za-z0-9_]) và "M" (thuộc tập [A-Za-z0-9_]) có "\b". Tức bạn tìm thấy hết.

Bạn thấy sự khác nhau giữa \s và \b chưa?

Ngoài ra vì \s là ký tự nên bạn "nhìn" thấy ký tự này trong đoạn khớp. Tức đoạn khớp không phải là "My" mà là " My ". Trong khi dùng \b thì bạn chả có thêm ký tự nào trong đoạn khớp, vì \b không là ký tự. Nó chỉ là vị trí mà người ta qui ước với nhau là cái chỗ đó chỗ đó gọi là \b. Thế thôi.

Tóm lại cứ hiểu nôm na là \s là ký tự cụ thể của một tập ký tự cụ thể, tức có thể cân, đo, đong, đếm và nhìn thấy được. Trong khi đó ^, $, \b là "khái niệm"

Thôi không quan trọng từ ngữ nữa. Dù là "khái niệm", "vị trí", "biên giới" thì cứ hiểu:
Nếu tìm thấy đoạn khớp mà:
- pattern = "\b\w..." thì trong text nguồn trước đoạn khớp đó không thể cũng là ký tự \w
- pattern = "...\w\b" thì trong text nguồn sau đoạn khớp đó không thể cũng là ký tự \w
- pattern = "\b\W..." thì trong text nguồn trước đoạn khớp đó không thể cũng là ký tự \W
- pattern = "...\W\b" thì trong text nguồn sau đoạn khớp đó không thể cũng là ký tự \W
Cái ^ và $ em đã hiểu từ khi xem cái bảng kí tự rồi , còn cái \b thì lúc đấy chưa nắm rõ năm nhưng theo mấy bài trên anh nói thì đã hiểu rồi ạ, thằng ^ và $ kiểu như dạng bắt buộc phải thực hiện từ đầu chuỗi hoặc cuối chuỗi , còn thằng \b thì rộng hơn chút nó là khoảng biên giới của các đoạn trong chuỗi, vì vậy trong một số trường hợp ^ và $ và \b có thể là như nhau. Còn \s em dùng nó như khoảng trắng thôi ạ, dù biết nó còn đại diện cho nhiều kí tự khác, do chưa dùng tới nên chỉ cần vậy là đủ. Cách kết hợp pattern này cũng không phải dễ, mỗi người có thể hiểu theo nhiểu cách khác nhau nhưng miễn sao hiểu đúng là được, đôi khi hiểu diễn giải ra thì thấy không hợp lý , do em cũng mới tìm hiểu đây nên còn nhiều cái chưa rõ nên được anh giải thích nên năm thêm được nhiều cái lắm ạ.
Em có chút thắc mắc trong cái ví dụ trên
text = "Mai tieu thu, Hong mat nau, My Ha noi choi than voi Hoa (Manh la ban cua ca hai)"
Anh dùng pattern="\bM\w*?\b" , dấu ? có bị dư không anh em nghĩ viết vầy "\bM\w*\b" cũng được, vì đã có * rồi sao lại thêm ?, * là xuất hiện >=0 lần, ? có thể xuất hiện hoặc không (0 hoặc 1 lần)
Nếu chỉ tách tên có chữ M như ví dụ của anh thì em nghĩ không cần dùng \b cũng được, có thể dùng các pattern này: "M\w*" hoặc "M\S*" (cài này có thể sai nếu các kí tự tập \S nằm cuối và kế là khoảng trắng)
Cám ơn anh đã nhiệt tình giải thích!!!
 
Lần chỉnh sửa cuối:
Upvote 0
Em có chút thắc mắc trong cái ví dụ trên
text = "Mai tieu thu, Hong mat nau, My Ha noi choi than voi Hoa (Manh la ban cua ca hai)"
Anh dùng pattern="\bM\w*?\b" , dấu ? có bị dư không anh em nghĩ viết vầy "\bM\w*\b" cũng được, vì đã có * rồi sao lại thêm ?, * là xuất hiện >=0 lần, ? có thể xuất hiện hoặc không (0 hoặc 1 lần)
Đúng là thừa. Tôi hì hục viết trong notepad, cũng không suy nghĩ nhiều.
Nếu chỉ tách tên có chữ M như ví dụ của anh thì em nghĩ không cần dùng \b cũng được, có thể dùng các pattern này: "M\w*" hoặc "M\S*"
Tôi không lặp lại thôi chứ xuyên suốt toàn bộ bài viết là tinh thần: "Tất nhiên có nhiều cách nhưng ta xét các cách cụ thể nhằm mục đích giải thích vài chuyện." đã viết ở điểm 3.
Ngoài ra tôi lấy vd. cụ thể như thế nhưng pattern phải là cho chuỗi tuỳ ý một chút.

Đúng là thừa \b cuối. Tức phải là Pattern = "\bM\w*"

Nếu
text = "Mai tieu thu, Hong mat nau, My Ha noi choi than voi Hoa (Manh la ban cua ca hai), con Ang dep trai thich Ai xinh gai"
và tìm các tên bắt đầu bằng A (hoặc a)
thì với pattern = "A\w*" hoặc pattern = "A\S*"

là sai rồi còn gì? Vd. như kết quả "ai" (từ Mai), "at" (từ mat), "au" (từ nau), "a" (từ Ha), "an" (từ than), "anh" (từ Manh) đâu có phải là kết quả mong đợi?

Còn nếu với pattern = "\bA\w*" thì chỉ tìm thấy "Ang", "Ai" thôi.

Tất nhiên tất cả chỉ là vd. đơn giản chỉ dùng với mục đích để giải thích và tìm hiểu về các mẫu pattern mà thôi. Trong thực tế ta phải phân tích: chuỗi nguồn có dạng thế nào, yêu cầu phải làm gì. Lúc đó mới có thể viết pattern.

Thực ra bài viết chỉ với mục đích giải thích cho bạn về ^, $, \s, \b chứ mục đích không phải là tìm các pattern ngắn gọn nhất, hay nhất, đẹp nhất. Vì thế tôi không suy nghĩ nhiều, không tập trung vào việc tìm pattern ngắn nhất, hay nhất. Chỉ lấy vd. đơn giản để giải thích về ^, $, \s, \b thôi. Khi bạn đã hiểu về chúng thì bạn có thể tự viết pattern cho mình, sửa pattern của mình, của tôi, của người khác sao cho ngắn gọn hơn.
 
Lần chỉnh sửa cuối:
Upvote 0
Đúng là thừa. Tôi hì hục viết trong notepad, cũng không suy nghĩ nhiều.

Tôi không lặp lại thôi chứ xuyên suốt toàn bộ bài viết là tinh thần: "Tất nhiên có nhiều cách nhưng ta xét các cách cụ thể nhằm mục đích giải thích vài chuyện." đã viết ở điểm 3.
Ngoài ra tôi lấy vd. cụ thể như thế nhưng pattern phải là cho chuỗi tuỳ ý một chút.

Đúng là thừa \b cuối. Tức phải là Pattern = "\bM\w*"

Nếu
text = "Mai tieu thu, Hong mat nau, My Ha noi choi than voi Hoa (Manh la ban cua ca hai), con Ang dep trai thich Ai xinh gai"
và tìm các tên bắt đầu bằng A (hoặc a)
thì với pattern = "A\w*" hoặc pattern = "A\S*"

là sai rồi còn gì? Vd. như kết quả "ai" (từ Mai), "at" (từ mat), "au" (từ nau), "a" (từ Ha), "an" (từ than), "anh" (từ Manh) đâu có phải là kết quả mong đợi?

Còn nếu với pattern = "\bA\w*" thì chỉ tìm thấy "Ang", "Ai" thôi.

Tất nhiên tất cả chỉ là vd. đơn giản chỉ dùng với mục đích để giải thích và tìm hiểu về các mẫu pattern mà thôi. Trong thực tế ta phải phân tích: chuỗi nguồn có dạng thế nào, yêu cầu phải làm gì. Lúc đó mới có thể viết pattern.

Thực ra bài viết chỉ với mục đích giải thích cho bạn về ^, $, \s, \b chứ mục đích không phải là tìm các pattern ngắn gọn nhất, hay nhất, đẹp nhất. Vì thế tôi không suy nghĩ nhiều, không tập trung vào việc tìm pattern ngắn nhất, hay nhất. Chỉ lấy vd. đơn giản để giải thích về ^, $, \s, \b thôi. Khi bạn đã hiểu về chúng thì bạn có thể tự viết pattern cho mình, sửa pattern của mình, của tôi, của người khác sao cho ngắn gọn hơn.
Vâng, cảm ơn anh nhiều ạ!!!
 
Upvote 0
Web KT
Back
Top Bottom