VNiTelex v1.21 - Hàm mã hóa giải mã chuỗi Unicode và Tiếng Việt cho mã VBA

Liên hệ QC

HeSanbi

Nam Nhân✨Hiếu Lễ Nghĩa Trí Tín✨
Tham gia
24/2/13
Bài viết
2,527
Được thích
3,818
Giới tính
Nam
Hôm nay tôi lại chia sẻ cho các bạn một hàm mã hóa chuỗi Unicode Tiếng Việt để dễ dàng viết chuỗi tiếng Việt vào trong VBA với kiểu gõ Telex đặc trưng của người Việt.

Lý do tại sao lại mã hóa? là vì trong VBA chỉ hỗ trợ lưu mã với một dạng mã hóa nhất định không hỗ trợ ký tự Unicode, nên cần phải chuyển mã thành mã phù hợp. Tuy nhiên việc gõ unicode trong mã VBA là rất rối, sinh mã dài. Mã hóa cũng giúp ngăn chặn xảy lỗi khi biên dịch hoặc thông dịch. Ngăn lỗi xảy ra tại hệ thống máy tính. Mã hóa cũng giúp tương thích với các kiểu dữ liệu khác như Json, javascript, hoặc python, ... . Nên việc mã hóa là rất cần thiết.

Dưới đây là hướng dẫn chi tiết.


Hàm VNiTelex - Mã hóa chuỗi Unicode tiếng Việt thành chuỗi trong VBA
Hàm này sẽ mã hóa tiếng Việt thành chuỗi của kiểu gõ Telex, và mã hóa các ký tự unicode khác thành chuỗi tương tự mã hóa trong Javascript là \uXXXX, hàm mã hóa này mã hóa tất cả ký tự Unicode có hỗ trợ.
Mã hóa Telex khác với cách gõ tay: Các ký tự thêm dấu (a, e o, w) và các ký tự xác định dấu (s, f, x, r, j) sẽ nằm sau cùng của từ và có dấu chấm phân tách. Ký tự xuống dòng sẽ mã hóa thành \n.
Ví dụ: với từ "ngừng" mã hóa thành "ngung.wf", phím thêm dấu "wf" phải nằm sau cùng và sau dấu chấm (.). Cách mã hóa này giúp dễ đọc chuỗi.
Ví dụ: với chuỗi "Xin chào cả gia đình" sẽ được mã hóa thành "Xin chao.f ca.r gia ddinh.f"

Sử dụng hàm đơn giản VNiTelex("chuỗi")

Mã hóa giải mã Unicode Hàn, Trung, Nga, Nhật, Ả-Rập, .... thành định dạng \uXXXX
나나 : 안녕하세요? 저는 나나예요.
Xin chào : Tôi là nana
마이클 : 안녕하세요? 저는 마이클 이에요.
Xin chào. Tồi là 마이클
나나 : 만나서 반가워요, 마이클 씨.
Rất vui được gặp bạn 마이클 씨..
마이클 : 반가워요. 나나 씨는 어느 나라 사람 이에요?
Rất vui được gặp. nana là người nước nào vậy?
나 나 : 저는 중국 사람 이에요.
Tôi là người Trung Quốc​
1. 请进!Qǐng jìn! Mời vào!
2. 你家真干净。
Nǐ jiā zhēn gānjìng.
Nhà bạn thật sạch sẽ.
3. 你坐这儿吧。
Nǐ zuò zhèr ba.
Bạn ngồi xuống đây đi.
4. 你们太客气了。
Nǐmen tài kèqìle.
Các bạn khách sáo quá.
5. 你喝什么?茶还是果汁?
Nǐ hē shénme? Chá háishì guǒzhī?
Bạn uống gì? Trà hay nước hoa quả?​
Xin chào Привет! (Privet)
Chào buổi sáng Доброе утро! (Dobroe utro)
Chào buổi chiều Добрый день!
Chào buổi tối Добрый вечер!
Chúc ngủ ngon Спокойной ночи!
Tên của bạn là gì? Как вас зовут?​
Xin chào‫مرحبً!‬
Xin chào‫مرحبًا! / نهارك سع!
Khỏe không? ‫كبف الحال؟ / كيف حالك؟‬
Bạn từ châu Âu đến à? ‫هل أنت من أوروبا؟‬
Bạn từ châu Mỹ đến à? ‫هل أنت من أمريكا؟‬​


Hàm VNiTelexDecode - Giải mã chuỗi mã hóa chuỗi Unicode Telex
Hàm giải mã chuỗi mã hóa Telex và unicode
Ví dụ: VNiTelexDecode("Xin chao.f ca.r gia ddinh.f")

Nếu không muốn mã hóa một đoạn trong chuỗi hãy sử dụng Đóng mở chuỗi như sau:
Hãy thêm khối mở {/ và đóng /} vào chuỗi nhập:
Ví dụ: VNiTelex("Xin chào cả {/gia đình/}") Thì chuỗi "gia đình" sẽ được loại trừ.
Ví dụ: VNiTelexDecode("{/Xin chao.f/} ca.r gia ddinh.f") Thì chuỗi "Xin chao.f" sẽ được loại trừ.

Phương thức: VNiTelexMultiDecode Để dịch ngược tổng thể để quá trình dịch ngược nhanh hơn.
Ví dụ:
JavaScript:
Sub Test1()
  Dim a1$, a2$, a3$
  a1= "xin chao.f"
  a2= "Viet.ej Nam"
  a3= "Lap.aj trinh.f VBA"
  VNiTelexMultiDecode a1, a2, a3
End Sub


Hàm VNiEscape - Mã hóa chuỗi Unicode javascript
Hàm mã hóa unicode thành định dạng \uXXXX
Ví dụ: VNiEscape("Chuỗi unicode")​
Thay vì mã hóa Telex như trên, hãy sử dụng hàm VNiEscape để mã hóa toàn bộ chuỗi unicode thành mã hóa đặc trưng của Json, javascript, python
Hàm VNiUnEscape - Giải mã chuỗi mã hóa chuỗi Unicode javascript
Hàm giải mã chuỗi mã hóa Telex và unicode
Ví dụ: VNiUnEscape ("Xin ch\u00E0o c\u1EA3 gia \u0111\u00ECnh")

Các bạn tham khảo thêm hàm MsgBox tiếng Việt có hỗ trợ hàm VNiTelex

Các bạn muốn mã hóa và chuyển mã trong VBA nhanh chóng có thể tham khảo tool hỗ trợ lập trình VBA:


VBAFastCode_Convert_Unicode


Các bạn có thể đọc thêm các bài viết của tôi tại tag #sanbi udf

Hãy chép mã dưới đây vào một Class Module mới để tiết kiệm bộ nhớ, vì khởi tạo sau khi ứng dụng chạy, hoặc chép vào module để sử dụng ngay, cách này làm tăng bộ nhớ.

Mã tham khảo:
JavaScript:
Option Explicit
'Option Private Module

Function VNiTelex(ByVal text As String, Optional oregex___ As Object, Optional odi___ As Object, Optional ByVal floor___%) As String
  'VNiTelex v1.2 : 03/02/2024
  Dim l&: l = Len(text): If l = 0 Then Exit Function
  Dim di2, m, ms, m0$, m1, m2$, m3$, aF, iba%, re, re2 As Object, v$, z$, ms2, m_, i1&, i2&, i3&
  Set re = CreateObject("VBScript.RegExp")
  With re:
    .Global = -1: .IgnoreCase = 0: .MultiLine = -1
    .pattern = "\u0110": text = .Replace(text, "Dd")
    .pattern = "\u0111": text = .Replace(text, "dd")
    .IgnoreCase = -1:
    ' Nêìu chuôÞi nãÌm trong cãòp {/ ... /} thiÌ không biò thay thêì
    .pattern = "(^|[^\\]?)\{/([^\u0008]*?)/\}"
  End With
 
  If odi___ Is Nothing Then
    Set odi___ = CreateObject("Scripting.Dictionary"): odi___.CompareMode = 0
    ' Các Phuò âm ghép
    '"(?:ngh|qu|tr|ch|th|nh|ng|ph|gi|kh|gh|r|t|s|d|h|l|x|c|v|b|n|m|[\u0111])"
    'AI, AO, AU, ÂU, AY, ÂY, EO, ÊU, IA, IÊU, YÊU, IU, OI, ÔI, ÕI, OAI, OAO, OAY, OEO, ÝA, UI, ÝI, ÝU, UÕ, UAI, UÂY, UÔI, ÝÕI, ÝÕU, UYA, UYU
    Dim ba1, ba2, angg, pat1, pat2
    pat1 = Array("a", "a", "a", "e", "e", "i", "o", "o", "o", "u", "u", "y")
    pat2 = Array("", "w", "a", "", "e", "", "", "o", "w", "", "w", "")
    ba1 = Array(Array(0, 259, 226, 0, 234, 0, 0, 244, 417, 0, 432, 0), _
                Array(225, 7855, 7845, 233, 7871, 237, 243, 7889, 7899, 250, 7913, 253), _
                Array(224, 7857, 7847, 232, 7873, 236, 242, 7891, 7901, 249, 7915, 7923), _
                Array(7843, 7859, 7849, 7867, 7875, 7881, 7887, 7893, 7903, 7911, 7917, 7927), _
                Array(227, 7861, 7851, 7869, 7877, 297, 245, 7895, 7905, 361, 7919, 7929), _
                Array(7841, 7863, 7853, 7865, 7879, 7883, 7885, 7897, 7907, 7909, 7921, 7925))
    ba2 = Array("", "s", "f", "r", "x", "j")
    For iba = 0 To 5:
      angg = ba1(iba)
      For m = 0 To 11:
        If angg(m) > 0 Then
          m0 = ChrW$(angg(m))
          odi___.add m0, Array(pat1(m), pat2(m), ba2(iba))
          odi___.add UCase$(m0), Array(UCase$(pat1(m)), pat2(m), ba2(iba))
        End If
      Next
    Next
    odi___.add "scrt", CreateObject("Scripting.Dictionary"): Set di2 = odi___("scrt")
  Else
    Set di2 = odi___("scrt"): If floor___ = 0 And di2.count Then di2.RemoveAll
  End If
  If oregex___ Is Nothing Then
    Set oregex___ = CreateObject("VBScript.RegExp")
    With oregex___:  .Global = -1: .IgnoreCase = -1: .MultiLine = -1
      Dim mpVowel$
      mpVowel = "\u1EC7\u00C0-\u00C3\u00C8-\u00CA\u00CC\u00CD\u00D2-\u00D5\u00D9\u00DA\u00DD\u00E0-\u00E3\u00E8-\u00EA\u00EC\u00ED\u00F2-\u00F5\u00F9\u00FA\u00FD\u0102\u0103\u0128\u0129\u0168\u0169\u01A0\u01A1\u01AF\u01B0\u1EA0-\u1EF9" '\u0110\u0111
      .pattern = "(([\u01B0]?[" & mpVowel & "])(ch|ng|nh|t|c|n|m|p|a|i|o|u|y|[\u01A0]|))"
    End With
    '
  End If
  Set ms2 = re.Execute(text)
  re.IgnoreCase = 0:
  If ms2.count Then
    i1 = 1
    For Each m_ In ms2
      v = m_.SubMatches(0)
      If v = Empty Then i2 = 1 Else i2 = 2
      i3 = m_.firstIndex + i2
      If i3 > i1 Then
        v = Mid$(text, i1, i3 - i1)
        GoSub r: z = z & VNiEscape(v, re2) & m_.SubMatches(1)
      Else
        z = z & m_.SubMatches(1)
      End If
      i1 = m_.firstIndex + m_.Length + 1
    Next
    If i1 <= l Then v = Mid$(text, i1): GoSub r: z = z & VNiEscape(v, re2)
  Else
    v = text: GoSub r: z = VNiEscape(v, re2)
  End If
  VNiTelex = z
Exit Function
r:
  If di2.count Then di2.RemoveAll
  Set ms = oregex___.Execute(v)
  If ms.count = 0 Then Return
  iba = 2:
l:
  For Each m1 In ms
    If Not di2.Exists(m1) Then
      m3 = m1.SubMatches(2)
      If Len(m3) = iba Then
        di2.add m1, "":  m2 = m1.SubMatches(1):
        If Len(m2) = 2 Then
          m0 = odi___(Left$(m2, 1))(0): aF = odi___(Right$(m2, 1))
        Else
          m0 = "": aF = odi___(m2)
        End If
        re.pattern = m1
        v = re.Replace(v, m0 & aF(0) & m3 & "." & aF(1) & aF(2))
      End If
    End If
  Next
  If iba > 0 Then iba = iba - 1: GoTo l
Return
End Function

Sub VNiTelexMultiDecode(ParamArray text())
  Dim i%, s$, p, v$
  v = vbBack & vbBack & "\" & vbBack
  p = text: s = join(p, v)
  p = Split(VNiTelexDecode(s), v)
  For i = 0 To UBound(text)
    text(i) = p(i)
  Next
End Sub
Function VNiTelexDecode(ByVal text As String, Optional oregex___ As Object, Optional odi___ As Object) As String
  'VNiTelex v1.22  29/01/2024
  Dim l&: l = Len(text): If l = 0 Then Exit Function
  Dim di2, x$, mm$, m, ms, m0$, m1$, m2$, m3$, m4, ba1, iba%, angg, re, re2 As Object, v$, z$, ms2, m_, i1&, i2&, i3&
  Set re = CreateObject("VBScript.RegExp")
  With re:  .Global = -1: .IgnoreCase = -1: .MultiLine = -1
    ' Nêìu chuôÞi nãÌm trong cãòp {/ ... /} thiÌ không biò thay thêì
    .pattern = "(^|[^\\]?)\{/([^\u0008]*?)/\}"
  End With

  Set odi___ = CreateObject("Scripting.Dictionary"): odi___.CompareMode = 1
  ' Các Phuò âm ghép
  '"(?:\b|[:_])(?:ngh|tr|ch|th|nh|ng|gi|qu|ph|kh|gh|r|t|s|d|h|l|x|c|v|b|n|m|[\u0111])"
  'AI, AO, AU, ÂU, AY, ÂY, EO, ÊU, IA, IÊU, YÊU, IU, OI, ÔI, ÕI, OAI, OAO, OAY, OEO, ÝA, UI, ÝI, ÝU, UÕ, UAI, UÂY, UÔI, ÝÕI, ÝÕU, UYA, UYU
  angg = Array("a", "aw", "aa", "e", "ee", "i", "o", "oo", "ow", "u", "uw", "y")
 
  For m = 0 To 11: odi___.add angg(m), m: Next
  ba1 = Array(Array(0, 259, 226, 0, 234, 0, 0, 244, 417, 0, 432, 0), _
              Array(225, 7855, 7845, 233, 7871, 237, 243, 7889, 7899, 250, 7913, 253), _
              Array(224, 7857, 7847, 232, 7873, 236, 242, 7891, 7901, 249, 7915, 7923), _
              Array(7843, 7859, 7849, 7867, 7875, 7881, 7887, 7893, 7903, 7911, 7917, 7927), _
              Array(227, 7861, 7851, 7869, 7877, 297, 245, 7895, 7905, 361, 7919, 7929), _
              Array(7841, 7863, 7853, 7865, 7879, 7883, 7885, 7897, 7907, 7909, 7921, 7925))
  For iba = 0 To 5:
    angg = ba1(iba)
    For m = 0 To 11:
      If angg(m) > 0 Then ba1(iba)(m) = ChrW$(angg(m)) Else ba1(iba)(m) = ""
    Next
  Next
  Set di2 = CreateObject("Scripting.Dictionary"): di2.CompareMode = 0


  Set oregex___ = CreateObject("VBScript.RegExp")
  With oregex___:  .Global = True: .IgnoreCase = True: .MultiLine = True
    .pattern = "((qu|gi|uo|[aeiouy])(ch|ng|nh|t|c|n|m|p|a|i|o|u|y|)\.([aweo][sfrxj]|[aweo]|[sfrxj]))"
  End With
 
  Set ms2 = re.Execute(text)
  If ms2.count Then
    i1 = 1
    For Each m_ In ms2
      v = m_.SubMatches(0)
      If v = Empty Then i2 = 1 Else i2 = 2
      i3 = m_.firstIndex + i2
      If i3 > i1 Then
        v = Mid$(text, i1, i3 - i1)
        GoSub r: z = z & v & m_.SubMatches(1)
      Else
        z = z & m_.SubMatches(1)
      End If
      i1 = m_.firstIndex + m_.Length + 1
    Next
    If i1 <= l Then v = Mid$(text, i1): GoSub r: z = z & v
  Else
    v = text: GoSub r: z = v
  End If
  VNiTelexDecode = z
Exit Function
r:
  If di2.count Then di2.RemoveAll
 

  v = VNiUnescape(v, re2)
 
  With re:
    .IgnoreCase = 0
    .pattern = "D[Dd]": v = .Replace(v, ChrW$(272))
    .pattern = "d[Dd]": v = .Replace(v, ChrW$(273))
    .IgnoreCase = 1
  End With

  Set ms = oregex___.Execute(v)
  If ms.count = 0 Then Return
  iba = 2
l:
  For Each m In ms
    x = m
 
    If Not di2.Exists(x) Then
   
      m1 = m.SubMatches(1)
      m2 = m.SubMatches(2)
      m3 = m.SubMatches(3)
      If StrComp(m1, "gi", 1) = 0 Then
        If m2 Like "[aiouyAIOUY]" Then
          m0 = m1: m1 = m2: m2 = ""
        Else
          m0 = Left$(m1, 1): m1 = Right$(m1, 1)
        End If
      ElseIf StrComp(m1, "qu", 1) = 0 Then
        m0 = m1: m1 = m2: m2 = ""
      ElseIf StrComp(m1, "uo", 1) = 0 Then
        If m3 Like "[wW]*" Then
          If m2 = Empty Then m0 = Left$(m1, 1) Else m0 = ChrW$(432 + (m1 Like "U*"))
        Else
          m0 = Left$(m1, 1)
        End If
        m1 = Right$(m1, 1)
      Else
          Debug.Print "x:"; x, m2  ' "(^|[^aueiyo])" &
        m0 = ""
      End If
      If m3 Like "[aAwWeEoO]*" Then m4 = m1 & Left$(m3, 1) Else m4 = m1
      If Len(m3) = iba Then
        di2.add x, x
        Select Case Right$(m3, 1)
        Case "s", "S": angg = ba1(1)
        Case "f", "F": angg = ba1(2)
        Case "r", "R": angg = ba1(3)
        Case "x", "X": angg = ba1(4)
        Case "j", "J": angg = ba1(5)
        Case Else: angg = ba1(0)
        End Select
        If UCase$(m1) <> m1 Then m2 = angg(odi___(m4)) & m2 Else m2 = UCase$(angg(odi___(m4))) & m2
        With re:
          If x Like "[aeiouyAEIOUY].*" Then
            .pattern = "(^|[^aueiyo])" & x: v = .Replace(v, "$1" & m0 & m2)
          Else
            .pattern = x: v = .Replace(v, mm & m0 & m2)
          End If
        End With
      End If
    End If
  Next
  If iba > 0 Then iba = iba - 1: GoTo l
Return
End Function


Function VNiUnescape(ByVal str$, Optional RegEx As Object)
  'VNiUnescape v1.2 : 03/02/2024 21:25
  Dim m, s, t, ms, d As Object
  On Error Resume Next

  If RegEx Is Nothing Then
    Set RegEx = CreateObject("VBScript.RegExp")
    With RegEx: .Global = True: .IgnoreCase = True: .MultiLine = False
      .pattern = "\\u([0-9a-fA-F]{4})"
    End With
  End If
  With RegEx
    s = Array("\\\\", "\\""", "\\/", "\\b", "\\f", "\\n", "\\r", "\\t")
    t = Array(vbBack & "{-}" & vbBack, """", "/", vbBack, vbFormFeed, vbLf, vbCr, vbTab)
    For m = 0 To 7
      .pattern = s(m): str = .Replace(str, t(m))
    Next
    Set d = CreateObject("Scripting.Dictionary")
    .pattern = "\\u([0-9a-fA-F]{4})"
    Set ms = RegEx.Execute(str)
    For Each m In ms
      t = m.SubMatches(0):
      If Not d.Exists(t) Then
        d.add t, ""
        .pattern = "\\u" & t
        str = .Replace(str, ChrW$(val("&H" & t)))
      End If
    Next m
    .pattern = "\u0008" & "\{-\}" & "\u0008"
    str = .Replace(str, "\")
  End With
  VNiUnescape = str
  Err.Clear
End Function


Function VNiEscape(ByVal str$, Optional RegEx As Object)
  'VNiEscape v1.2 : 03/02/2024 21:25

  If RegEx Is Nothing Then Set RegEx = CreateObject("VBScript.RegExp")
 
  On Error Resume Next
  Dim s, t$, i&, k&, a, h$
  With RegEx
    .Global = True: .IgnoreCase = False: .MultiLine = False
    .pattern = "(.)(?=.*\1)"
    s = .Replace(str, "")
    .pattern = "\\"
    str = .Replace(str, "\\")
    For i = 1 To Len(s)
      t = Mid$(s, i, 1): a = AscW(t) And 65535
      Select Case a
      Case 1 To 127:
      Case Else:
        h = Hex(a): h = "\u" & String(4 - Len(h), "0") & h
        .pattern = h: str = .Replace(str, h)
      End Select
    Next
    a = Array("""", "/", "\u0008", "\u000C", "\u000A", "\u000D", "\u0009")
    s = Array("""""", "\/", "\b", "\f", "\n", "\r", "\t")
    For i = 0 To 6
      .pattern = a(i): str = .Replace(str, s(i))
    Next
  End With
  VNiEscape = str
  Err.Clear
End Function


Function Replace2(ByVal Expression$, ByVal Find$, ByVal Replace As String, Optional compare As Boolean) As String
  'Replace2 v1.2 : 03/02/2024 21:25
  On Error Resume Next
  With CreateObject("VBScript.RegExp")
    .Global = True: .IgnoreCase = compare: .MultiLine = False
    .pattern = Find
    Expression = .Replace(Expression, Replace)
  End With
  Replace2 = Expression
  Err.Clear
End Function

Các bạn có thể đặt mã vào Class Module để gọi sau sẽ tiết kiệm bộ nhớ.
Trong tệp có sẵn class Module clsVNiUnicode, chỉ cần kéo thả về dự án của bạn để sử dụng.
Các gọi nhanh:
Mã:
 clsVNiUnicode.VNiTelexMultiDecode a1, a2 ,a3
set clsVNiUnicode = nothing
 

File đính kèm

  • VNIUnicodeTelex_v1.21.xlsm
    120.1 KB · Đọc: 11
Lần chỉnh sửa cuối:
Giải pháp
***** Cập nhật v1.21 *****
Sửa một sai sót khi dịch ngược ký tự .
Thêm phương thức: VNiTelexMultiDecode Để dịch ngược tổng thể để quá trình dịch ngược nhanh hơn.
Ví dụ:
JavaScript:
Sub Test1()
  Dim a1$, a2$, a3$
  a1= "xin chao.f"
  a2= "Viet.ej Nam"
  a3= "Lap.aj trinh.f VBA"
  VNiTelexMultiDecode a1, a2, a3
End Sub
***** Cập nhật v1.2 *****

Nếu không muốn mã hóa một đoạn trong chuỗi hãy sử dụng Đóng mở chuỗi như sau:

Hãy thêm khối mở {/ và đóng /} vào chuỗi nhập:​
Ví dụ: VNiTelex("Xin chào cả {/gia đình/}") Thì chuỗi "gia đình" sẽ được loại trừ.
Ví dụ: VNiTelexDecode("{/Xin chao.f/} ca.r gia ddinh.f") Thì chuỗi "Xin chao.f" sẽ được loại trừ.
 
Lần chỉnh sửa cuối:
Upvote 0
Cập nhật mã: Loại bỏ các hàm Replace của VBA trong mã thành phương thức Replace của thư viện Regular Expression cải thiện hiệu xuất.
 
Lần chỉnh sửa cuối:
Upvote 0
***** Cập nhật v1.21 *****
Sửa một sai sót khi dịch ngược ký tự .
Thêm phương thức: VNiTelexMultiDecode Để dịch ngược tổng thể để quá trình dịch ngược nhanh hơn.
Ví dụ:
JavaScript:
Sub Test1()
  Dim a1$, a2$, a3$
  a1= "xin chao.f"
  a2= "Viet.ej Nam"
  a3= "Lap.aj trinh.f VBA"
  VNiTelexMultiDecode a1, a2, a3
End Sub
 
Upvote 0
Giải pháp
Tinh chỉnh lại code cho mượt hơn

Mã:
Function VNiTelex(ByVal text As String, Optional oregex___ As Object, Optional odi___ As Object, Optional ByVal floor___ As Long = 0) As String
    Dim l As Long: l = Len(text): If l = 0 Then Exit Function
    Dim di2, m, ms, m0, m1, m2, m3, aF, iba As Long, re As Object, v As String, z As String
    Set re = CreateObject("VBScript.RegExp")
    With re
        .Global = True: .IgnoreCase = False: .MultiLine = True
        .Pattern = "\u0110": text = .Replace(text, "Dd")
        .Pattern = "\u0111": text = .Replace(text, "dd")
        .IgnoreCase = True
        .Pattern = "(^|[^\\]?)\{/([^\u0008]*?)/\}"
    End With

    ' Initialize dictionary if it's not passed
    If odi___ Is Nothing Then
        Set odi___ = CreateObject("Scripting.Dictionary")
        odi___.CompareMode = 0
        
        ' Initialize phonetic patterns
        Dim pat1, pat2, ba1, angg As Variant
        pat1 = Array("a", "a", "a", "e", "e", "i", "o", "o", "o", "u", "u", "y")
        pat2 = Array("", "w", "a", "", "e", "", "", "o", "w", "", "w", "")
        ba1 = Array( _
            Array(0, 259, 226, 0, 234, 0, 0, 244, 417, 0, 432, 0), _
            Array(225, 7855, 7845, 233, 7871, 237, 243, 7889, 7899, 250, 7913, 253), _
            Array(224, 7857, 7847, 232, 7873, 236, 242, 7891, 7901, 249, 7915, 7923), _
            Array(7843, 7859, 7849, 7867, 7875, 7881, 7887, 7893, 7903, 7911, 7917, 7927), _
            Array(227, 7861, 7851, 7869, 7877, 297, 245, 7895, 7905, 361, 7919, 7929), _
            Array(7841, 7863, 7853, 7865, 7879, 7883, 7885, 7897, 7907, 7909, 7921, 7925))
        
        ' Build phonetic dictionary
        For iba = 0 To 5
            angg = ba1(iba)
            For m = LBound(angg) To UBound(angg)
                If angg(m) > 0 Then
                    m0 = ChrW$(angg(m))
                    odi___.Add m0, Array(pat1(m), pat2(m))
                    odi___.Add UCase$(m0), Array(UCase$(pat1(m)), pat2(m))
                End If
            Next
        Next
        odi___.Add "scrt", CreateObject("Scripting.Dictionary")
    End If

    Dim ms2 As Object
    Set ms2 = re.Execute(text)
    re.IgnoreCase = False

    If ms2.Count > 0 Then
        Dim i1 As Long: i1 = 1
        For Each m In ms2
            v = m.SubMatches(0)
            Dim i2 As Long: i2 = IIf(v = Empty, 1, 2)
            Dim i3 As Long: i3 = m.FirstIndex + i2
            
            If i3 > i1 Then
                v = Mid$(text, i1, i3 - i1)
                GoSub r
                z = z & VNiEscape(v) & m.SubMatches(1)
            Else
                z = z & m.SubMatches(1)
            End If
            
            i1 = m.FirstIndex + m.Length + 1
        Next
        If i1 <= l Then
            v = Mid$(text, i1)
            GoSub r
            z = z & VNiEscape(v)
        End If
    Else
        v = text
        GoSub r
        z = VNiEscape(v)
    End If
    
    VNiTelex = z
    Exit Function

r:
    If di2.Count Then di2.RemoveAll
    Set ms = oregex___Execute(v)
    If ms.Count = 0 Then Return
    
    iba = 2
l:
    For Each m1 In ms
        If Not di2.Exists(m1) Then
            m3 = m1.SubMatches(2)
            If Len(m3) = iba Then
                di2.Add m1, ""
                m2 = m1.SubMatches(1)
                m0 = IIf(Len(m2) = 2, odi___(Left$(m2, 1))(0), "")
                aF = odi___(Right$(m2, 1))
                re.Pattern = m1
                v = re.Replace(v, m0 & aF(0) & m3 & "." & aF(1))
            End If
        End If
    Next
    If iba > 0 Then iba = iba - 1: GoTo l
Return

End Function

Sub VNiTelexMultiDecode(ParamArray text())
    Dim i As Long, s As String, p, v As String
    v = vbBack & vbBack & "\" & vbBack
    p = Join(text, v)
    p = Split(VNiTelexDecode(s), v)
    For i = 0 To UBound(text)
        text(i) = p(i)
    Next
End Sub

Function VNiTelexDecode(ByVal text As String, Optional oregex___ As Object, Optional odi___ As Object) As String
    Dim l As Long: l = Len(text): If l = 0 Then Exit Function
    Dim di2 As Object, v As String, z As String
    Set di2 = CreateObject("Scripting.Dictionary")
    di2.CompareMode = 0
    Dim re As Object, ms As Object
    Set re = CreateObject("VBScript.RegExp")
    
    With re
        .Global = True: .IgnoreCase = True: .MultiLine = True
        .Pattern = "(^|[^\\]?)\{/([^\u0008]*?)/\}"
    End With
    
    ' Additional configuration omitted for brevity...
    
    Set ms2 = re.Execute(text)
    If ms2.Count > 0 Then
        Dim i1 As Long: i1 = 1
        For Each m In ms2
            v = m.SubMatches(0)
            Dim i2 As Long: i2 = IIf(v = Empty, 1, 2)
            Dim i3 As Long: i3 = m.FirstIndex + i2
            
            If i3 > i1 Then
                v = Mid$(text, i1, i3 - i1)
                GoSub r
                z = z & v & m.SubMatches(1)
            Else
                z = z & m.SubMatches(1)
            End If
            
            i1 = m.FirstIndex + m.Length + 1
        Next
        
        If i1 <= l Then
            v = Mid$(text, i1)
            GoSub r
            z = z & v
        End If
    Else
        v = text
        GoSub r
        z = v
    End If
    
    VNiTelexDecode = z
    Exit Function

r:
    If di2.Count Then di2.RemoveAll
    v = VNiUnescape(v)
    
    With re
        .IgnoreCase = False
        .Pattern = "D[Dd]": v = .Replace(v, ChrW$(272))
        .Pattern = "d[Dd]": v = .Replace(v, ChrW$(273))
        .IgnoreCase = True
    End With

    Set ms = oregex___.Execute(v)
    If ms.Count = 0 Then Return

    iba = 2
l:
    For Each m In ms
        ' Processing logic...
    Next
    If iba > 0 Then iba = iba - 1: GoTo l
Return
End Function
 
Upvote 0
Cách dùng đơn giản hơn, em từng sưu tầm được trên GPE
1725504821723.png
Mã:
Function T_Uni(MyStr As String) As String
'Ham thay doi TV qua uni code
  Application.Volatile
Dim str As String, CStart As Integer, CCount As Integer, Status As Boolean
str = "-7842-7843-7841-259-7855-7857-7859-7861-7863-7845-7847-7849-7851-7853-273-7867-7869-7865-7871-7873-7875-7877-7879-7881-297-7883-7887-7885-7889-7891-7893-7895-7897-417-7899-7901-7903-7905-7907-7911-361-7909-432-7913-7915-7917-7919-7921-7923-7927-7929-7925-7840-258-7854-7856-7858-7860-7862-7844-7846-7848-7850-7852-272-7866-7868-7864-7870-7872-7874-7876-7878-7880-296-7882-7886-7884-7888-7890-7892-7894-7896-416-7898-7900-7902-7904-7906-7910-360-7908-431-7912-7914-7916-7918-7920-7922-7926-7928-7924-10-"
For i = 1 To Len(MyStr)
If InStr(str, "-" & AscW(Mid(MyStr, i, 1)) & "-") = 0 Then
    If Not Status Then
        CStart = i:        Status = True
    End If
    CCount = CCount + 1
Else
    If Status Then T_Uni = T_Uni & IIf(T_Uni = "", "", " & ") & """" & Mid(MyStr, CStart, CCount) & """"
    Status = False
    CCount = 0
    T_Uni = T_Uni & IIf(T_Uni = "", "", " & ") & "ChrW(" & AscW(Mid(MyStr, i, 1)) & ")"
End If
Next
If Status Then T_Uni = T_Uni & IIf(T_Uni = "", "", " & ") & """" & Mid(MyStr, CStart, CCount) & """"
End Function
 
Upvote 0
Cách dùng đơn giản hơn, em từng sưu tầm được trên GPE

Bạn nên biết sâu hơn về ký tự thì mới biết mình đang học hỏi đúng hay sai.
Ký tự "á" và "à" trong mã đó khi ghi vào code VBA thì nó không phải là ký tự "á" và "à" Unicode.
Mã của bạn đề xuất không có khả năng mã hóa các ký tự khác ngoài tiếng Việt.
Dịch mã sang hàm ChrW là tốn kém và khó đọc.
 
Upvote 0
Vâng, em thật sự chưa biết sâu hơn thật, em thấy đáp ứng đúng cái em cần nên chia sẻ lại mang tính tham khảo cho những anh/chị đang đi tìm giải pháp, không có ý phản bác hay dẫn dắt đi xa code của anh, anh thông cảm. Cảm ơn anh đã giải thích.
 
Upvote 0
Web KT

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

Back
Top Bottom