Hàm Proper (Unicode) tự tạo (1 người xem)

Liên hệ QC

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

Hoàng Trọng Nghĩa

Chuyên gia GPE
Thành viên BQT
Moderator
Tham gia
17/8/08
Bài viết
8,662
Được thích
16,725
Giới tính
Nam
Tôi mới cải tiến chút về hàm này, không biết có cách nào nhanh hơn và gọn hơn hay không? Ở đây là kết hợp hàm Proper và hàm Trim nên thấy hơi dài dòng, nhưng lại hiệu quả.

Các bạn tham khảo:

PHP:
Function MyUniProper(MyTextUni As String) As String
    Dim MyChar As String, MyProper As String, LenChar As Long, Tmp As String
    MyTextUni = " " & LCase(MyTextUni)
    For LenChar = 2 To Len(MyTextUni)
        MyChar = Mid(MyTextUni, LenChar, 1)
        If Mid(MyTextUni, LenChar - 1, 1) = " " Then
            If AscW(MyChar) < 256 Then MyChar = UCase(MyChar) Else MyChar = ChrW(AscW(MyChar) - 1)
        End If
        MyProper = MyProper & MyChar
    Next
    Tmp = Trim(MyProper)
        Do While InStr(Tmp, Space(2))
            Tmp = Replace(Tmp, Space(2), Space(1))
        Loop
    MyUniProper = Tmp
End Function
 
Tôi mới cải tiến chút về hàm này, không biết có cách nào nhanh hơn và gọn hơn hay không? Ở đây là kết hợp hàm Proper và hàm Trim nên thấy hơi dài dòng, nhưng lại hiệu quả.

Các bạn tham khảo:

PHP:
Function MyUniProper(MyTextUni As String) As String
    Dim MyChar As String, MyProper As String, LenChar As Long, Tmp As String
    MyTextUni = " " & LCase(MyTextUni)
    For LenChar = 2 To Len(MyTextUni)
        MyChar = Mid(MyTextUni, LenChar, 1)
        If Mid(MyTextUni, LenChar - 1, 1) = " " Then
            If AscW(MyChar) < 256 Then MyChar = UCase(MyChar) Else MyChar = ChrW(AscW(MyChar) - 1)
        End If
        MyProper = MyProper & MyChar
    Next
    Tmp = Trim(MyProper)
        Do While InStr(Tmp, Space(2))
            Tmp = Replace(Tmp, Space(2), Space(1))
        Loop
    MyUniProper = Tmp
End Function
Thật ra 3 hàm UPPER, LOWER và PROPER không đơn giản thế đâu
Thí nghiệm 3 hàm này trên Excel sẽ thấy nó làm việc được với Range và cả Array nữa đấy
Nếu xây dựng hàm làm việc với Uniode, hãy viết theo hướng này... Ví dụ:
PHP:
Private Function ChangeCaseFromString(ByVal Text As String, ByVal CaseType As Long) As String
  Dim Arr, i As Long
  On Error Resume Next
  If Trim(Text) <> "" And Not (IsNumeric(Text)) Then
    Select Case CaseType
      Case 1: ChangeCaseFromString = LCase(Text)
      Case 2: ChangeCaseFromString = UCase(Text)
      Case 3
        If InStr(Trim(Text), " ") Then
          Arr = Split(Text, " ")
          For i = 0 To UBound(Arr)
            If Trim(Arr(i)) <> "" Then _
              Arr(i) = UCase(Left(Trim(Arr(i)), 1)) & LCase(Mid(Trim(Arr(i)), 2, Len(Arr(i))))
          Next
          ChangeCaseFromString = Join(Arr, " ")
        Else
          ChangeCaseFromString = UCase(Left(Trim(Text), 1)) & LCase(Mid(Trim(Text), 2, Len(Text)))
        End If
    End Select
  Else
    ChangeCaseFromString = Text
  End If
End Function
PHP:
Function ChangeCase(ByVal sArray, ByVal CaseType As Long)
  Dim TmpArr, TmpStr As String, i As Long, j As Long
  On Error Resume Next
  TmpArr = sArray
  If TypeName(TmpArr) <> "Variant()" Then
    TmpArr = ChangeCaseFromString(TmpArr, CaseType)
  Else
    TmpStr = Join(TmpArr, " ")
    If TmpStr <> "" Then
      For i = LBound(TmpArr) To UBound(TmpArr)
        TmpArr(i) = ChangeCaseFromString(TmpArr(i), CaseType)
      Next
    Else
      For i = LBound(TmpArr, 1) To UBound(TmpArr, 1)
        For j = LBound(TmpArr, 2) To UBound(TmpArr, 2)
          TmpArr(i, j) = ChangeCaseFromString(TmpArr(i, j), CaseType)
        Next
      Next
    End If
  End If
  ChangeCase = TmpArr
End Function
Trong đó hàm ChangeCaseFromString là hàm hổ trợ và ChangeCase là hàm chính
Áp dụng:
=ChangeCase(A1:A4,3) ---> Proper cho vùng A1:A4
=ChangeCase({"cộng hòa","chủ nghĩa";"xã hội","việt nam"},3) ---> Proper cho 1 Array
vân vân...
------------------
Có thể áp dụng hàm trên để ChangeCase dữ liệu trên ListBox của UserForm chẳng hạn
 
Upvote 0
Sao hàm nào cũng có kết quả sai tại đây vậy ta: d.a.p thay vì ra D.A.P thì lại ra kết quả là D.a.p

Lẽ ra sau dấu chấm (.) thì phải viết hoa chứ nhỉ?

Thôi thì cứ mượn hàm PROPER trong Excel làm thì gọn hơn!
 
Lần chỉnh sửa cuối:
Upvote 0
Upvote 0
Theo nguyên tắc thì sau dấu chấm phải có một khoảng trắng.

Vậy đó, nhưng PROPER trong Excel nó hiểu hết đó! Vậy làm theo hàm này ngắn gọn vậy!

Mã:
Function PCase(MyTextUni As String) As String
    Dim stt As Long
    MyTextUni = " " & MyTrim(LCase(MyTextUni))
    stt = Len(MyTextUni)
    If stt > 1 Then
        Do
            stt = InStrRev(MyTextUni, " ", stt)
            Mid(MyTextUni, stt + 1, 1) = UCase(Mid(MyTextUni, stt + 1, 1))
            stt = stt - 1
        Loop While stt > 0
        [COLOR=#ff0000][B]PCase = WorksheetFunction.Proper(Mid(MyTextUni, 2))[/B][/COLOR]
    End If
End Function
 
Upvote 0
Vậy đó, nhưng PROPER trong Excel nó hiểu hết đó! Vậy làm theo hàm này ngắn gọn vậy!

Mã:
Function PCase(MyTextUni As String) As String
    Dim stt As Long
    MyTextUni = " " & MyTrim(LCase(MyTextUni))
    stt = Len(MyTextUni)
    If stt > 1 Then
        Do
            stt = InStrRev(MyTextUni, " ", stt)
            Mid(MyTextUni, stt + 1, 1) = UCase(Mid(MyTextUni, stt + 1, 1))
            stt = stt - 1
        Loop While stt > 0
        [COLOR=#ff0000][B]PCase = WorksheetFunction.Proper(Mid(MyTextUni, 2))[/B][/COLOR]
    End If
End Function
Bạn có thể sửa code sau cho nó dùng được với dấu chấm... chứ dùng PROPER thì cũng như không... Nó đâu có chính xác với chuổi tiếng Việt Unicode
Trong code của bạn có thằng MyTrim(LCase(MyTextUni))... chẳng hiểu MyTrim là giống gì
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn có thể sửa code sau cho nó dùng được với dấu chấm... chứ dùng PROPER thì cũng như không... Nó đâu có chính xác với chuổi tiếng Việt Unicode
Trong code của bạn có thằng MyTrim(LCase(MyTextUni))... chẳng hiểu MyTrim là giống gì

Sorry, cái đó do dùng Replace nên bị "dính chưởng" luôn, phải như vầy: Trim(LCase(MyTextUni))


Mã:
[/B]Function PCase(MyTextUni As String) As String
    Dim stt As Long
    MyTextUni = " " & Trim(LCase(MyTextUni))
    stt = Len(MyTextUni)
    If stt > 1 Then
        Do
            stt = InStrRev(MyTextUni, " ", stt)
            Mid(MyTextUni, stt + 1, 1) = UCase(Mid(MyTextUni, stt + 1, 1))
            stt = stt - 1
        Loop While stt > 0
        PCase = WorksheetFunction.Proper(Mid(MyTextUni, 2))
    End If
End Function

[B]

Với hàm này bảo đảm không bị sót chữ đâu Thầy ơi, em thử mọi trường hợp về dấu rồi mà! Nhưng nếu dùng không PROPER trong sheet thì bị lỗi nhiều đấy!
 
Lần chỉnh sửa cuối:
Upvote 0
Hàm proper xài tốt cho Unicode tiếng Anh. (Ẹc ẹc, mở bài thôi)
Unicode tiếng Việt chỉ có rất ít ký tự có dấu đứng đầu 1 từ: Ú, Ứ, Ấ, Ẩ, Ă, Ê, Ế, ... (ký tự sau không quan tâm (ẹc ẹc, dẫn đề đó)

Vậy, chỉ cần dùng Proper của Excel + 1 mảng 2 chiều, + 1 vòng lặp Replace từ đầu mảng đến cuối mảng là xong.
Cũng như đọc số thành chữ Unicode, đâu cần thay hết 6, 7 chục ký tự có dấu tiếng Việt đâu? Chỉ thay 1 số ký tự xài trong các từ đọc số thôi. (Cái này là thân bài)

Kết luận: Càng đơn giản càng tốt.
 
Upvote 0
Với hàm này bảo đảm không bị sót chữ đâu Thầy ơi, em thử mọi trường hợp về dấu rồi mà! Nhưng nếu dùng không PROPER trong sheet thì bị lỗi nhiều đấy!
Bạn đang cải tiến để dùng với dấu chấm, vậy ta thí nghiệm với dấu chấm đây:
=PCase("ẩ.ổ")
xem nó ra cái gì?
 
Upvote 0
Bạn đang cải tiến để dùng với dấu chấm, vậy ta thí nghiệm với dấu chấm đây:
=PCase("ẩ.ổ")
xem nó ra cái gì?

Chưa thấy trường hợp nào viết tắt có dấu ở nguyên âm. Vậy trường hợp này đương nhiên bị loại trừ, nhưng ví dụ như đường Phan Đình Phùng, viết tắt p.đ.p nó hiểu ngay là P.Đ.P

Xin xem file.
 

File đính kèm

Upvote 0
Chưa thấy trường hợp nào viết tắt có dấu ở nguyên âm. Vậy trường hợp này đương nhiên bị loại trừ, nhưng ví dụ như đường Phan Đình Phùng, viết tắt p.đ.p nó hiểu ngay là P.Đ.P

Xin xem file.
Chưa thấy không có nghĩa là không có
Hàm thì phải tổng quát chứ
Ẹc... Ẹc... Sửa lại đi đồng chí ơi
Nói thiệt, hàm tôi viết ở trên, nếu muốn chơi thêm dấu chấm cũng chỉ sửa cái rẹt là xong!
Tóm lại: Chơi với PROPER là.. THUA
 
Upvote 0
Chưa thấy không có nghĩa là không có
Hàm thì phải tổng quát chứ
Ẹc... Ẹc... Sửa lại đi đồng chí ơi
Nói thiệt, hàm tôi viết ở trên, nếu muốn chơi thêm dấu chấm cũng chỉ sửa cái rẹt là xong!
Tóm lại: Chơi với PROPER là.. THUA

Không đâu Thầy ơi, với dấu đặc biệt như gạch ngang (-) gạch nối (_), dấu sao (*), dấu xuyệt/xẹt (/) ... thì nó phân biệt được hết chứ không riêng gì dấu chấm (.)
 
Lần chỉnh sửa cuối:
Upvote 0
Không đâu Thầy ơi, với dấu đặc biệt như gạch ngang (-) gạch nối (_), dấu sao (*), dấu xuyệt/xẹt (/) ... thì nó phân biệt được hết chứ không riêng gì dấu chấm (.)
Cũng chẳng có vấn đề gì
Lý nào làm được với dấu cách còn mấy dấu khác lại làm không xong!
 
Upvote 0
Em không biết phương pháp này làm như thế nào, có thể dựa vào hàm này chăng: AscW()
Để ý sẽ thấy rằng: Toàn bộ các ký tự đặc biệt đều có đặc điểm LCase("Ký tự đặc biệt") = UCase("Ký tự đặc biệt")
Vậy thì theo thuật toán này mà làm... ví dụ thế này:
PHP:
tmp = Trim(Text)
If Len(tmp) = 1 Then
  ChangeCaseFromString = UCase(tmp)
Else
  tmp = UCase(Left(tmp, 1)) & LCase(Mid(tmp, 2, Len(tmp)))
  For i = 2 To Len(tmp)
    If UCase(Mid(tmp, i, 1)) <> LCase(Mid(tmp, i, 1)) Then
      If UCase(Mid(tmp, i - 1, 1)) = LCase(Mid(tmp, i - 1, 1)) Then
        tmp = Left(tmp, i - 1) & Replace(tmp, Mid(tmp, i, 1), UCase(Mid(tmp, i, 1)), i, 1)
      End If
    End If
  Next
  ChangeCaseFromString = tmp
End If
Với Text là biến đầu vào
 
Upvote 0
Để ý sẽ thấy rằng: Toàn bộ các ký tự đặc biệt đều có đặc điểm LCase("Ký tự đặc biệt") = UCase("Ký tự đặc biệt")
Vậy thì theo thuật toán này mà làm... ví dụ thế này:
PHP:
tmp = Trim(Text)
If Len(tmp) = 1 Then
  ChangeCaseFromString = UCase(tmp)
Else
  tmp = UCase(Left(tmp, 1)) & LCase(Mid(tmp, 2, Len(tmp)))
  For i = 2 To Len(tmp)
    If UCase(Mid(tmp, i, 1)) <> LCase(Mid(tmp, i, 1)) Then
      If UCase(Mid(tmp, i - 1, 1)) = LCase(Mid(tmp, i - 1, 1)) Then
        tmp = Left(tmp, i - 1) & Replace(tmp, Mid(tmp, i, 1), UCase(Mid(tmp, i, 1)), i, 1)
      End If
    End If
  Next
  ChangeCaseFromString = tmp
End If
Với Text là biến đầu vào

Tuyệt vời quá, đã kiểm tra và thật như ý! Cám ơn Thầy nhiều! Cho em cập nhật mới hàm ChangeCaseFromString của Thầy nhé:

Hàm bổ trợ:
Mã:
Private Function ChangeCaseFromString(ByVal Text As String, ByVal CaseType As Long) As String
    Dim Arr, i As Long
    On Error Resume Next
    If Trim(Text) <> "" And Not (IsNumeric(Text)) Then
        Select Case CaseType
            Case 1: ChangeCaseFromString = LCase(Text)
            Case 2: ChangeCaseFromString = UCase(Text)
            Case 3
            tmp = Trim(Text)
            If Len(tmp) = 1 Then
                ChangeCaseFromString = UCase(tmp)
            Else
                tmp = UCase(Left(tmp, 1)) & LCase(Mid(tmp, 2, Len(tmp)))
                For i = 2 To Len(tmp)
                    If UCase(Mid(tmp, i, 1)) <> LCase(Mid(tmp, i, 1)) Then
                        If UCase(Mid(tmp, i - 1, 1)) = LCase(Mid(tmp, i - 1, 1)) Then
                            tmp = Left(tmp, i - 1) & Replace(tmp, Mid(tmp, i, 1), UCase(Mid(tmp, i, 1)), i, 1)
                        End If
                    End If
                Next
                ChangeCaseFromString = tmp
            End If
        End Select
    End If
End Function

Hàm sử dụng (không đổi):
Mã:
Function ChangeCase(ByVal sArray, ByVal CaseType As Long)
    Dim TmpArr, TmpStr As String, i As Long, j As Long
    On Error Resume Next
    TmpArr = sArray
    If TypeName(TmpArr) <> "Variant()" Then
        TmpArr = ChangeCaseFromString(TmpArr, CaseType)
    Else
        TmpStr = Join(TmpArr, " ")
        If TmpStr <> "" Then
            For i = LBound(TmpArr) To UBound(TmpArr)
                TmpArr(i) = ChangeCaseFromString(TmpArr(i), CaseType)
            Next
        Else
            For i = LBound(TmpArr, 1) To UBound(TmpArr, 1)
                For j = LBound(TmpArr, 2) To UBound(TmpArr, 2)
                    TmpArr(i, j) = ChangeCaseFromString(TmpArr(i, j), CaseType)
                Next
            Next
        End If
    End If
    ChangeCase = TmpArr
End Function

Cách sử dụng:
Trong đó hàm ChangeCaseFromString là hàm hổ trợ và ChangeCase là hàm chính
Áp dụng:

=ChangeCase(A1:A4,3) ---> Proper cho vùng A1:A4

=ChangeCase({"cộng hòa","chủ nghĩa";"xã hội","việt nam"},3)
---> Proper cho 1 Array
vân vân...
------------------
Có thể áp dụng hàm trên để ChangeCase dữ liệu trên ListBox của UserForm chẳng hạn
 
Upvote 0
Tuyệt vời quá, đã kiểm tra và thật như ý! Cám ơn Thầy nhiều! Cho em cập nhật mới hàm ChangeCaseFromString của Thầy nhé:

Hàm bổ trợ:
Mã:
Private Function ChangeCaseFromString(ByVal Text As String, ByVal CaseType As Long) As String
    Dim Arr, i As Long
    On Error Resume Next
    If Trim(Text) <> "" And Not (IsNumeric(Text)) Then
        Select Case CaseType
            Case 1: ChangeCaseFromString = LCase(Text)
            Case 2: ChangeCaseFromString = UCase(Text)
            Case 3
            tmp = Trim(Text)
            If Len(tmp) = 1 Then
                ChangeCaseFromString = UCase(tmp)
            Else
                tmp = UCase(Left(tmp, 1)) & LCase(Mid(tmp, 2, Len(tmp)))
                For i = 2 To Len(tmp)
                    If UCase(Mid(tmp, i, 1)) <> LCase(Mid(tmp, i, 1)) Then
                        If UCase(Mid(tmp, i - 1, 1)) = LCase(Mid(tmp, i - 1, 1)) Then
                            tmp = Left(tmp, i - 1) & Replace(tmp, Mid(tmp, i, 1), UCase(Mid(tmp, i, 1)), i, 1)
                        End If
                    End If
                Next
                ChangeCaseFromString = tmp
            End If
        End Select
    End If
End Function

Buồn buồn ngồi Test thử hàm trên (kiểu PROPER) và ngẫm nghĩ lại, thấy thời gian của hàm này ... chậm quá!

Tôi test như sau:

Tạo một biến kiểu chuỗi toàn cục, viết thủ tục để tạo dãy chuỗi ký tự cho biến đó:

Tại Module1 đặt hàm ChangeCaseFromString trên vào trong.

Tại Module2 đặt thủ tục test:

Mã:
[COLOR=#0000ff]''Dòng đầu tiên:[/COLOR]
Public Str As String



[COLOR=#0000ff]''Code tạo chuỗi ([/COLOR][COLOR=#ff0000]chỉ cho chạy 1 lần[/COLOR][COLOR=#0000ff]):[/COLOR]
Sub CreateString()
    Dim i As Long
    Str = ""
    For i = 1 To 5000
        Str = Str + "hoang trong.n.g.h.i.a ng......hi.......a..... "
    Next
End Sub



[COLOR=#0000ff]''Code test thời gian:[/COLOR]
Sub Test_Old_Version()
    Dim t As Double
    t = Timer
        Dim Text As String
        Text = ChangeCaseFromString(Str, 3)
    Debug.Print "2/ " & Timer - t
End Sub

Kết quả thời gian xử lý cho chuỗi đó trên máy tôi: 88.2890625 giây!

Bây giờ tôi sẽ cải tiến hàm đó bằng cách xử lý chuỗi theo cách tách ký tự của chuỗi đầu vào ra từng ký tự, gán các ký tự được tách này vào một mảng một chiều, sau đó xử lý mỗi phần tử của mảng này:

Hàm đã được cải tiến: ChangeCaseFromString_HTN

Tôi đặt hàm này vào Module1:

[GPECODE=vb]
Public Enum CaseType
Lower = 1
Upper = 2
Proper = 3
End Enum


''NEW VERSION:
Function ChangeCaseFromString_HTN(ByVal UnicodeString As String, Optional ByVal CaseOfType As CaseType) As String
If UnicodeString = "" Then Exit Function
Select Case CaseOfType
Case 1 'LOWER:
ChangeCaseFromString_HTN = LCase(UnicodeString)
Case 2 'UPPER:
ChangeCaseFromString_HTN = UCase(UnicodeString)
Case 3 'PROPER:
Dim ArrChar(), i As Long
UnicodeString = UCase(UnicodeString)
ReDim ArrChar(1 To Len(UnicodeString))
ArrChar(1) = Left(UnicodeString, 1)
For i = 2 To Len(UnicodeString)
ArrChar(i) = Mid(UnicodeString, i, 1)
If UCase(ArrChar(i - 1)) <> LCase(ArrChar(i - 1)) Then
ArrChar(i) = LCase(ArrChar(i))
End If
Next
ChangeCaseFromString_HTN = Join(ArrChar, "")
Case Else 'Return input string:
ChangeCaseFromString_HTN = UnicodeString
End Select
End Function
[/GPECODE]

Và đặt thủ tục test vào Module2:

Mã:
Sub Test_New_Version()
    Dim t As Double
    t = Timer
        Dim Text As String
        Text = ChangeCaseFromString_HTN(Str, Proper)
    Debug.Print "1/ " & Timer - t
End Sub

Kết quả của hàm cải tiến có thời gian xử lý là: 1.089075 giây!

------------------------------------------------------------------

KẾT LUẬN:

1) Hàm mới và hàm cũ có tỷ lệ thời gian: 1/88

2) XỬ LÝ TRỰC TIẾP TRÊN CHUỖI (STRING) LUÔN LUÔN CHẬM HƠN XỬ LÝ THÔNG QUA MẢNG (ARRAY).

 

File đính kèm

Lần chỉnh sửa cuối:
Upvote 0

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

Back
Top Bottom