Tặng UDF Function: Hàm đọc số thành chữ Tiếng Anh

Liên hệ QC

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,590
Được thích
16,653
Giới tính
Nam
Nhằm đáp ứng nhu cầu của topic này:

Chào các bạn.

Mình tìm nhiều bài viết trên diễn đàn nhưng chủ yếu là viết code đọc chữ tiếng anh cho các đơn vị tính (tiền tệ, khối lượng...).
Mình mới học VBA nên chưa thể sửa được các code đó (thường là không có chữ AND như ở dưới).

Các bạn viết giúp code chỉ đọc số tiếng Anh bình thường

Ví dụ:
14,040: FORTEEN THOUSAND AND FORTY
4,001: FOUR THOUSAND AND ONE
9,700: NINE THOUSAND SEVEN HUNDRED.
25,469.87 : TWENTY FIVE THOUSAND FOUR HUNDRED AND SIXTY NINE POINT EIGHTY SEVEN


Tôi đã nghiên cứu cách đọc từ nhiều nguồn (thậm chí trong từ điển Oxford Dictionary) và cụ thể là theo cách đọc của trang Global Education (http://www.globaledu.com.vn/Thong-Tin-Chi-Tiet/2683/Tong-hop-ve-cach-doc-cac-so-lieu-trong-tieng-Anh).

Vấn đề cách đọc phần thập phân, tôi cũng nghiên cứu như sau:

Example 1.

.038
"38 thousandths"

Ignore the decimal point and read 038 as the whole number "Thirty-eight." The last digit, 8, falls in the thousandths place.

When we read .038 as "Point 0, 3, 8," that is "spelling" the number, which is often convenient. But its name is "Thirty-eight thousandths".

Nguồn: http://www.themathpage.com/arith/decimals-2.htm

Tôi chọn cách đọc thuận tiện (point zero, three, eight) hơn là cách đọc quy cách (Thirty-eight thousandths) nếu đọc đúng quy cách thật là khó nhận dạng!

Tôi xin tặng các bạn hàm đọc số thành chữ tiếng Anh ReadEnglishNumber. Hàm này đọc được số thập phân (tôi giới hạn đọc 3 số, các bạn có thể mở rộng hơn nếu muốn).

Với hàm này, tôi nghĩ người biết sơ về Array cũng có thể dễ dàng hiểu được cách tôi viết và cũng dễ dàng chỉnh sửa theo ý muốn của mình (có And hay không And, A hay One đầu câu, phẩy hay không phẩy v.v...).

Đây là hàm chính:

Mã:
Option Explicit
[COLOR=#008000][B]'***************************************************************************************************
'' Author: Hoang Trong Nghia - GiaiphapExcel.com
'' Version: V.1.0 - 5/5/2013
'***************************************************************************************************[/B]
[/COLOR]
Function ReadEnglishNumber(ByVal Series As String) As String
    If Not IsNumeric(Series) Then Exit Function
    Series = Replace(Series, " ", "")
    If Series = "" Then Exit Function
    If Series = 0 Then ReadEnglishNumber = "Zero.": Exit Function
    If Series >= 1E+15 Then ReadEnglishNumber = "No result (huge number).": Exit Function
    Dim arrUnits, Deci As String, Digi As String
    arrUnits = DecimalSpelling(Series)
    Digi = arrUnits(0): Deci = arrUnits(1)
    Dim DigitString, SplitArr, SplitArray, Ubd As Long, i As Long, JoinArr(), n As Long
    DigitString = Array("Hundred", " thousand", " million", " billion", " trillion")
    SplitArray = Split(Digi, ",")
    Ubd = UBound(SplitArray)
    ReDim SplitArr(0 To Ubd)
    For i = Ubd To 0 Step -1
        SplitArr(n) = SplitArray(i)
        n = n + 1
    Next
    Dim Itm As String: n = 0
    For i = Ubd To 0 Step -1
        Itm = SplitArr(i)
        If Itm > 0 Then
            If i = 0 Then
                ReDim Preserve JoinArr(0 To n)
                JoinArr(n) = Hundreds(Itm)
            Else
                ReDim Preserve JoinArr(0 To n)
                JoinArr(n) = Hundreds(Itm) & DigitString(i)
            End If
            n = n + 1
        End If
    Next
    Digi = Join(JoinArr, ", ")
    If Left(Digi, 4) = "one " Then
        Digi = Replace(Digi, "one ", "A ", , 1)
    Else
        Digi = UCase(Left(Digi, 1)) & Mid(Digi, 2)
    End If
    ReadEnglishNumber = Digi & Deci & "."
End Function

Các hàm hỗ trợ:

Hàm đọc phần nguyên:

Mã:
[COLOR=#008000]''This is a supplemental function for 'ReadEnglishNumber' function:[/COLOR]
Private Function Hundreds(ByVal StrNum As String) As String
    Dim Units, LessThanTwenty, Tens
    Units = Array("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
    LessThanTwenty = Array("ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", _
                           "seventeen", "eighteen", "nineteen")
    Tens = Array("twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety")
    Dim Len1 As String, Len2 As String, Len3 As String
    Select Case Len(StrNum)
    Case 1
        Hundreds = Units(StrNum)
    Case 2
        Select Case StrNum
        Case Is < 20
            Hundreds = LessThanTwenty(StrNum - 10)
        Case Else
            If StrNum Mod 10 = 0 Then
                Hundreds = Tens(StrNum / 10 - 2)
            Else
                Len1 = Right(StrNum, 1)
                Len2 = (StrNum - Len1) / 10 - 2
                Hundreds = Tens(Len2) & "-" & Units(Len1)
            End If
        End Select
    Case Else
        Len3 = Left(StrNum, 1)
        Hundreds = Units(Len3) & " hundred"
        Len3 = Val(Right(StrNum, 2))
        
        If Len3 > 0 Then
            Dim Hdrs As String
            Select Case Len(Len3)
            Case 1
                Hdrs = Units(Len3)
            Case 2
                Select Case Len3
                Case Is < 20
                    Hdrs = LessThanTwenty(Len3 - 10)
                Case Else
                    If StrNum Mod 10 = 0 Then
                        Hdrs = Tens(Len3 / 10 - 2)
                    Else
                        Len1 = Right(Len3, 1)
                        Len2 = (Len3 - Len1) / 10 - 2
                        Hdrs = Tens(Len2) & "-" & Units(Len1)
                    End If
                End Select
            End Select
            Hundreds = Hundreds & " and " & Hdrs
        End If
    End Select
End Function

Hàm đọc phần thập phân:

Mã:
[COLOR=#008000]''This is a supplemental function for 'ReadEnglishNumber' function:[/COLOR]
Private Function DecimalSpelling(ByVal Series As String) As Variant
    Dim DeciSpell(0 To 1)
    Dim Point As Long, Deci As String, Digi As String
    Point = InStr(Series, "."): Digi = Series
    If Point = 0 Then
GoTo ExitFunction
    Else
        Deci = Mid(Series, Point)
        If Deci = 0 Then
GoTo ExitFunction
        Else
            Digi = Replace(Series, Deci, "")
            If Len(Deci) > 4 Then
                Deci = Format(Deci, "0.000")
                If Deci = 0 Then
GoTo ExitFunction
                ElseIf Deci = 1 Then
                    DeciSpell(0) = Format(Digi + 1, "#,##0")
                    DeciSpell(1) = ""
                    DecimalSpelling = DeciSpell
                    Exit Function
                Else
                    Deci = Replace(Deci, "0.", "")
                End If
            Else
                Deci = Replace(Deci, ".", "")
            End If
            Dim Units, arrUnits, Tens, i As Long, j As Long
            Units = Array("zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine")
            j = Len(Deci)
            ReDim arrUnits(1 To j)
            For i = 1 To j
                arrUnits(i) = Units(Mid(Deci, i, 1))
            Next
            DeciSpell(0) = Format(Digi, "#,##0")
            DeciSpell(1) = " point " & Join(arrUnits, ", ")
        End If
    End If
    DecimalSpelling = DeciSpell
    Exit Function
ExitFunction:
    DeciSpell(0) = Format(Digi, "#,##0")
    DeciSpell(1) = ""
    DecimalSpelling = DeciSpell
End Function

Sau khi các bạn tải file về, test mọi kiểu, nếu phát hiện lỗi hay cần bổ sung những gì thì gửi bài lên đây tôi sẽ điều chỉnh lại.

Hoàng Trọng Nghĩa.


============================================================

Đã có file update cho việc đọc số âm (Negative Numbers). Các bạn nên tải file NumberConvertHTN_V.1.1_Minus.xls để sử dụng.
 

File đính kèm

  • NumberConvertHTN_V.1.0.xls
    67 KB · Đọc: 153
  • NumberConvertHTN_V.1.1_Minus.xls
    68.5 KB · Đọc: 203
Lần chỉnh sửa cuối:
Lắm lúc người ta lại nói, "tôi đi xe đò mất 36 tiếng 45 phút", đại loại là như vậy thì cái hàm nó "tửng từng tưng" luôn!

Theo em nghĩ, chẳng cần bận tâm đến số thập phân, và trong công việc của em cũng thế. Em chẳng bao giờ nói 1 tấn 300 ký, mà chỉ ghi 1,3 tấn, tiếng Anh thì 1.3 mts, thế thôi (chưa kể là Long Ton (1,016.047 kg), Short Ton (907.1847 kg), 2 cái đơn vị đều là Ton hết, quy ra số lẻ cũng ná thở).

Chiều cao cũng thế, cao 1 mét 60 em ghi 1,6 mét, chẳng cần quy ra 1 mét 6 decimet hay 1 mét 60 centimet hoặc 1 mét 600 milimet làm gì!

"Em" không xài, không phải là không ai xài, em ạ. Trên GPE đã có người hỏi rồi.

Trường hợp không phải hệ thập phân như yard/foot/inch, hour/minute thì cũng không khó, nhưng ít ai cần dùng trên báo cáo (chưa ai hỏi). Coi như hàm này chỉ dùng cho hệ thập phân.
 
Upvote 0
Ý mình là như thế này: thực ra tham số t4 ko thật cần vì nếu có phần thập phân và có đơn vị cho nó thì thường mình sẽ xác định đc lấy chữ số thứ bn trong phần thập phân để đọc
Thay cho ReadE(1.265, "meter", "centimeter", 2) chỉ cần viết ReadE(1.265, "meter", "centimeter") vẫn đọc như nhau One meter and twenty six point five centimeters
Như vậy cũng hạn chế gõ nhầm ở t/số 4. Tuy nhiên hàm cũng bị dài thêm 1 ít với phần quy đổi chèn vào.
Có gì mong mọi ng chỉ bảo thêm
 
Upvote 0
Ý mình là như thế này: thực ra tham số t4 ko thật cần vì nếu có phần thập phân và có đơn vị cho nó thì thường mình sẽ xác định đc lấy chữ số thứ bn trong phần thập phân để đọc
Thay cho ReadE(1.265, "meter", "centimeter", 2) chỉ cần viết ReadE(1.265, "meter", "centimeter") vẫn đọc như nhau One meter and twenty six point five centimeters
Như vậy cũng hạn chế gõ nhầm ở t/số 4. Tuy nhiên hàm cũng bị dài thêm 1 ít với phần quy đổi chèn vào.
Có gì mong mọi ng chỉ bảo thêm

Nếu không có tham số thứ 4, bạn nghĩ rằng người viết hàm sẽ phải liệt kê tất cả các cặp đơn vị tính m/dm, m/cm, m/mm, ton/kg, kg/g, l/ml, l/cl, m3/cm3, m3/dm3, USD/cent, Pound/Penny, .... (hàng trăm cặp) à?

Rồi bỗng người dùng muốn đọc 1 cặp số chưa liệt kê thì sao?

Ngoài ra, giả sử người viết hàm liệt kê đúng chính tả là meter/centimeter, nhưng người dùng viết sai chính tả meter/centimetter thì cũng vẫn lỗi như thường.

Cho nên tôi mới viết ở trên: Người dùng phải tự biết kiến thức cơ bản, không phải cái gì máy cũng làm thay.

Đồng thời tôi cũng viết bên topic kia:

Vả lại, đọc tới mức thập phân nào, người dùng tự quyết định. Không cần lường trước mọi cái thuộc chủ quan con người. Tôi viết hàm đọc số, tôi có hướng dẫn sử dụng. Sử dụng đúng mà kết quả sai, tôi chịu trách nhiệm. Sử dụng sai, tự chịu trách nhiệm.

Dùng hàm vlookup bị #Value! thì có phải tại anh Bill không?
 
Lần chỉnh sửa cuối:
Upvote 0
Bạn ơi, Bạn có hàm đọc số thập phân bang tiếng Việt Unicode không?
 
Upvote 0
Với hàm trước tôi chưa chú ý đến vấn đề số âm (Negative numbers), hôm nay tôi cập nhật thêm phiên bản mới bao gồm đọc luôn cả số âm.

Cách đọc số âm trong tiếng Anh cũng khá giống cách đọc của tiếng Việt:

Ví dụ: số -9 thì có hai cách đọc:

+) Đọc "negative nine" tương đương với cách đọc trong Tiếng Việt chúng ta là: "âm chín".

+) Đọc "minus nine" tương đương với cách đọc trong Tiếng Việt là: "trừ chín".

Hai cách đọc đó cũng giống nhau cả.

Tuy nhiên tôi sẽ chọn cách đọc dùng "Minus" vì nó có tính phổ biến hơn là từ kia (nhưng các bạn có thể thay thế "Negative" thay cho nó trong hàm nếu các bạn thích điều đó).

Sau đây là hàm mới cập nhật:

Mã:
Option Explicit
[COLOR=#008000][B]'***************************************************************************************************
'' Author: Hoang Trong Nghia - GiaiphapExcel.com
'' Version: V.1.1 - 6/5/2013
'***************************************************************************************************
[/B][/COLOR]
Function ReadEnglishNumber(ByVal Series As String) As String
    Series = Replace(Series, " ", "")
    If Not IsNumeric(Series) Then Exit Function
    Dim IsNegative As Boolean
[COLOR=#ff0000]    If Left(Series, 1) = "-" Then
        IsNegative = True
        Series = Replace(Series, "-", "")
    End If
[/COLOR]    If Series = "" Then Exit Function
    If Series = 0 Then ReadEnglishNumber = "Zero.": Exit Function
    If Series >= 1E+15 Then ReadEnglishNumber = "No result (huge number).": Exit Function
    Dim arrUnits, Deci As String, Digi As String
    arrUnits = DecimalSpelling(Series)
    Digi = arrUnits(0): Deci = arrUnits(1)
    Dim DigitString, SplitArr, SplitArray, Ubd As Long, i As Long, JoinArr(), n As Long
    DigitString = Array("Hundred", " thousand", " million", " billion", " trillion")
    SplitArray = Split(Digi, ",")
    Ubd = UBound(SplitArray)
    ReDim SplitArr(0 To Ubd)
    For i = Ubd To 0 Step -1
        SplitArr(n) = SplitArray(i)
        n = n + 1
    Next
    Dim Itm As String: n = 0
    For i = Ubd To 0 Step -1
        Itm = SplitArr(i)
        If Itm > 0 Then
            If i = 0 Then
                ReDim Preserve JoinArr(0 To n)
                JoinArr(n) = Hundreds(Itm)
            Else
                ReDim Preserve JoinArr(0 To n)
                JoinArr(n) = Hundreds(Itm) & DigitString(i)
            End If
            n = n + 1
        End If
    Next
    Digi = Join(JoinArr, ", ")
[COLOR=#ff0000]    If IsNegative Then
        Digi = "Minus " & Digi[/COLOR][COLOR=#008000] 'You can use "Negative" instead of "Minus"[/COLOR][COLOR=#ff0000]
[/COLOR]    Else
        If Left(Digi, 4) = "one " Then
            Digi = Replace(Digi, "one ", "A ", , 1)
        Else
            Digi = UCase(Left(Digi, 1)) & Mid(Digi, 2)
        End If
    End If
    ReadEnglishNumber = Digi & Deci & "."
End Function

Các bạn nên tải file NumberConvertHTN_V.1.1_Minus.xls về để test hoặc sử dụng tại bài 1 của topic này.
Em thấy trong file này NumberConvertHTN_V.1.1_Minus.xls số 100 và 1000 đọc giống nhau (tương tự số 200 và 2000, 300 và 3000...) vậy anh?
 
Upvote 0
Upvote 0
Upvote 0
chào mọi người ạ, bây giờ em muốn chuyển phần thập phân thành số ví dụ : 412.41 -> chữ sẽ là : fourty hundred and twelve and fourty one cent như thế nào ạ! Em cảm ơn mọi người nhiều!
 
Upvote 0
Web KT
Back
Top Bottom