Tạo code VBA đổi số thành chữ trong Excel

Chọn cách đọc, tạo module VBA và sao chép vào Excel.

Bạn cần hàm cho

Số thường tạo hàm DOCSO. Số tiền tạo hàm DOCVND, ghi “đồng” ở cuối.

Cách đọc
Kiểu chữ
Đổi tên hàm (nâng cao)

Chỉ chữ cái a–z không dấu, chữ số và dấu gạch dưới; bắt đầu bằng chữ cái; tối đa 30 ký tự. Không trùng hàm có sẵn của Excel, địa chỉ ô (như DOC1) hoặc từ khóa VBA.

DOCSO()
  • Số thường
  • nghìn, linh, mốt, bốn, lăm
  • Viết hoa chữ đầu
' ============================================================
' Generated by sothanhchu.com - Vietnamese number to words (VBA)
' Function: DOCSO(value) - whole numbers only
' Mode: number (whole numbers only)
' Words: nghin / linh / mot / bon / lam / ty
' Case: sentence
' No network access, no file access, no shell commands, no add-ins.
' Plain ASCII source: Vietnamese letters are built with ChrW().
' Input: a number below 1E+15, or text made only of digits.
' Anything else (decimals, 1E+15 or more, non-numeric text) returns #VALUE!.
' ============================================================
Option Explicit
Private Const STC_IS_MONEY As Boolean = False
Private Const STC_OMIT_ZERO_HUNDRED As Boolean = False
Private Const STC_CAPITALIZE As Boolean = True
Private Const STC_MAX_DIGITS As Long = 36
Private Const STC_ERR_VALUE As Long = 2015 ' #VALUE!

Private stcReady As Boolean
Private stcDigit(0 To 9) As String
Private stcHundred As String, stcTen As String, stcTens As String
Private stcThousand As String, stcMillion As String, stcBillion As String
Private stcZeroTens As String, stcOne As String, stcFour As String, stcFive As String
Private stcNeg As String, stcUnit As String, stcWhole As String

Public Function DOCSO(ByVal stcValue As Variant) As Variant
    DOCSO = stcConvert(stcValue)
End Function

Private Sub stcInit()
    If stcReady Then Exit Sub
    stcDigit(0) = "kh" & ChrW(244) & "ng"   ' khong
    stcDigit(1) = "m" & ChrW(7897) & "t"   ' mot
    stcDigit(2) = "hai"   ' hai
    stcDigit(3) = "ba"   ' ba
    stcDigit(4) = "b" & ChrW(7889) & "n"   ' bon
    stcDigit(5) = "n" & ChrW(259) & "m"   ' nam
    stcDigit(6) = "s" & ChrW(225) & "u"   ' sau
    stcDigit(7) = "b" & ChrW(7843) & "y"   ' bay
    stcDigit(8) = "t" & ChrW(225) & "m"   ' tam
    stcDigit(9) = "ch" & ChrW(237) & "n"   ' chin
    stcHundred = "tr" & ChrW(259) & "m"   ' tram
    stcTen = "m" & ChrW(432) & ChrW(7901) & "i"   ' muoi
    stcTens = "m" & ChrW(432) & ChrW(417) & "i"   ' muoi
    stcThousand = "ngh" & ChrW(236) & "n"   ' nghin
    stcMillion = "tri" & ChrW(7879) & "u"   ' trieu
    stcBillion = "t" & ChrW(7927)   ' ty
    stcZeroTens = "linh"   ' linh
    stcOne = "m" & ChrW(7889) & "t"   ' mot
    stcFour = "b" & ChrW(7889) & "n"   ' bon
    stcFive = "l" & ChrW(259) & "m"   ' lam
    stcNeg = ChrW(226) & "m"   ' am
    stcUnit = ""
    stcWhole = ""
    stcReady = True
End Sub

Private Function stcConvert(ByVal v As Variant) As Variant
    Dim neg As Boolean, digits As String, s As String, cellRef As Object
    If IsObject(v) Then ' a cell reference can arrive as a Range object
        Set cellRef = v
        v = cellRef.Value
    End If
    If IsError(v) Then stcConvert = v: Exit Function
    If IsArray(v) Then stcConvert = CVErr(STC_ERR_VALUE): Exit Function
    If IsEmpty(v) Or IsNull(v) Then stcConvert = "": Exit Function
    If VarType(v) = vbString Then
        If Len(Trim$(v)) = 0 Then stcConvert = "": Exit Function
    End If
    If Not stcParse(v, neg, digits) Then stcConvert = CVErr(STC_ERR_VALUE): Exit Function
    stcInit
    s = stcIntegerWords(digits)
    If neg Then s = stcNeg & " " & s
    If STC_IS_MONEY Then
        s = s & " " & stcUnit
        If Len(stcWhole) > 0 Then s = s & " " & stcWhole
    End If
    If STC_CAPITALIZE Then s = stcCapFirst(s)
    stcConvert = s
End Function

' Accepts a whole number: a numeric cell below 1E+15, or text made only of digits (optional leading minus).
Private Function stcParse(ByVal v As Variant, ByRef neg As Boolean, ByRef digits As String) As Boolean
    Dim s As String, i As Long, c As Long
    neg = False
    digits = ""
    Select Case VarType(v)
        Case vbByte, vbInteger, vbLong, vbSingle, vbDouble, vbCurrency, vbDecimal
            If Abs(v) >= 1E+15 Then Exit Function
            If v <> Fix(v) Then Exit Function
            neg = (v < 0)
            digits = Format$(Abs(v), "0")
        Case vbString
            s = Trim$(v)
            If Len(s) > 255 Then Exit Function
            If Left$(s, 1) = "-" Then
                neg = True
                s = Mid$(s, 2)
            End If
            If Len(s) = 0 Then Exit Function
            For i = 1 To Len(s)
                c = AscW(Mid$(s, i, 1))
                If c < 48 Or c > 57 Then Exit Function
            Next i
            digits = s
        Case Else
            Exit Function
    End Select
    digits = stcTrimZeros(digits)
    If Len(digits) > STC_MAX_DIGITS Then Exit Function
    If Len(digits) = 0 Then neg = False
    stcParse = True
End Function

Private Function stcTrimZeros(ByVal s As String) As String
    Dim i As Long
    i = 1
    Do While i <= Len(s)
        If Mid$(s, i, 1) <> "0" Then Exit Do
        i = i + 1
    Loop
    stcTrimZeros = Mid$(s, i)
End Function

Private Sub stcAdd(ByRef s As String, ByVal w As String)
    If Len(w) = 0 Then Exit Sub
    If Len(s) > 0 Then s = s & " "
    s = s & w
End Sub

' Whole number (digits only) -> words. Groups of three digits; from 10^9 on the number is read in
' nine-digit chunks, each followed by the billion word once per chunk level (nghin ty, trieu ty, ty ty ...).
Private Function stcIntegerWords(ByVal digits As String) As String
    Dim cnt As Long, i As Long, g As Long, chunk As Long, pos As Long, lowerG As Long, k As Long, topIdx As Long
    Dim lowest As Boolean, piece As String, outText As String
    Dim vals() As Long
    If Len(digits) = 0 Then stcIntegerWords = stcDigit(0): Exit Function
    cnt = (Len(digits) + 2) \ 3
    digits = String$(cnt * 3 - Len(digits), "0") & digits
    ReDim vals(0 To cnt - 1)
    topIdx = -1
    For i = 0 To cnt - 1
        vals(i) = CLng(Mid$(digits, i * 3 + 1, 3))
        If topIdx < 0 And vals(i) > 0 Then topIdx = i
    Next i
    For i = 0 To cnt - 1
        If vals(i) > 0 Then
            g = cnt - 1 - i
            pos = g Mod 3
            chunk = g \ 3
            piece = stcGroupWords(vals(i), (i = topIdx))
            If pos = 1 Then stcAdd piece, stcThousand
            If pos = 2 Then stcAdd piece, stcMillion
            If chunk > 0 Then
                lowest = True
                For lowerG = chunk * 3 To g - 1
                    If vals(cnt - 1 - lowerG) > 0 Then lowest = False
                Next lowerG
                If lowest Then
                    For k = 1 To chunk
                        stcAdd piece, stcBillion
                    Next k
                End If
            End If
            stcAdd outText, piece
        End If
    Next i
    stcIntegerWords = outText
End Function

' One group of three digits (1..999). isTop = the most significant non-zero group (no leading "khong tram").
Private Function stcGroupWords(ByVal v As Long, ByVal isTop As Boolean) As String
    Dim h As Long, t As Long, u As Long, zeroTensCtx As Boolean, s As String
    h = v \ 100
    t = (v \ 10) Mod 10
    u = v Mod 10
    If h > 0 Then
        stcAdd s, stcDigit(h)
        stcAdd s, stcHundred
    ElseIf (Not isTop) And (Not STC_OMIT_ZERO_HUNDRED) Then
        stcAdd s, stcDigit(0)
        stcAdd s, stcHundred
    End If
    zeroTensCtx = (t = 0) And (u > 0) And ((h > 0) Or (Not isTop))
    If t = 0 Then
        If zeroTensCtx Then stcAdd s, stcZeroTens
    ElseIf t = 1 Then
        stcAdd s, stcTen
    Else
        stcAdd s, stcDigit(t)
        stcAdd s, stcTens
    End If
    If u > 0 Then
        If u = 1 And t >= 2 Then
            stcAdd s, stcOne
        ElseIf u = 4 And (t >= 2 Or zeroTensCtx) Then
            stcAdd s, stcFour
        ElseIf u = 5 And t >= 1 Then
            stcAdd s, stcFive
        Else
            stcAdd s, stcDigit(u)
        End If
    End If
    stcGroupWords = s
End Function

' Upper-case the first letter only. Every Vietnamese reading starts with an ASCII letter or the letter a-circumflex.
Private Function stcCapFirst(ByVal s As String) As String
    Dim c As Long
    If Len(s) = 0 Then Exit Function
    c = AscW(Left$(s, 1))
    If c = 226 Then
        stcCapFirst = ChrW(194) & Mid$(s, 2)
    ElseIf c < 128 Then
        stcCapFirst = UCase$(Left$(s, 1)) & Mid$(s, 2)
    Else
        stcCapFirst = s
    End If
End Function

Chưa chạy thử trong Microsoft Excel thật. Mã đã được chạy bằng LibreOffice (chế độ tương thích VBA). Sau khi dán, đối chiếu bảng “Kiểm tra sau khi cài” bên dưới. Ô số chỉ nhận số nguyên dưới 1E+15; số dài nhập dưới dạng văn bản.

Mã chạy hoàn toàn trong Excel của bạn, không cần mạng. Đọc qua trước khi dán: cách tự kiểm tra.

Kiểm tra sau khi cài

Gõ từng công thức vào một ô Excel. Kết quả phải giống cột bên phải; nếu khác, xem lỗi thường gặp.

Công thức thử và kết quả mong đợi theo cấu hình đã chọn
Gõ vào ô ExcelPhải ra
=DOCSO(21)Hai mươi mốt
=DOCSO(105)Một trăm linh năm
=DOCSO(1250000)Một triệu hai trăm năm mươi nghìn
=DOCSO(24)Hai mươi bốn
=DOCSO(25)Hai mươi lăm
=DOCSO(1005)Một nghìn không trăm linh năm
=DOCSO("123456789012345678")Một trăm hai mươi ba triệu bốn trăm năm mươi sáu nghìn bảy trăm tám mươi chín tỷ không trăm mười hai triệu ba trăm bốn mươi lăm nghìn sáu trăm bảy mươi támSố 18 chữ số nằm trong dấu ngoặc kép: đọc đủ mọi chữ số.
=DOCSO(1.5)#VALUE!Số thập phân không được hỗ trợ (xem “Mã hoạt động thế nào”).

Mã được tạo ngay trên máy bạn. Lựa chọn của bạn không được gửi đi và không lưu lại.

Cài vào Excel trên Windows

  1. Mở file Excel cần dùng hàm (hoặc tạo file mới).
  2. Mở trình soạn VBA bằng Alt+F11. Cách khác: tab Developer > Visual Basic (tab Developer bị ẩn mặc định; bật trong File > Options > Customize Ribbon).
  3. Trong trình soạn, vào Insert > Module. Một cửa sổ trắng hiện ra.
  4. Dán mã vừa sao chép vào cửa sổ đó. Nếu cửa sổ đã có sẵn dòng Option Explicit, xóa dòng ấy trước khi dán để khỏi bị lặp.
  5. Đóng trình soạn VBA (không cần chạy gì) và quay lại bảng tính. Gõ =DOCSO(A1) vào một ô, rồi so với mục Kiểm tra sau khi cài.
  6. Lưu file ở định dạng .xlsm (mục dưới). Bước này dễ bị bỏ quên.

Tên menu theo ngôn ngữ giao diện Excel của bạn; ở Excel tiếng Việt các mục có thể mang tên khác. Nguồn: hướng dẫn của Microsoft về tạo macro.

Lưu file đúng cách, kẻo mất mã

File .xlsx thông thường không chứa được mã VBA. Nếu bạn dán mã rồi lưu thành .xlsx, lần mở sau hàm sẽ biến mất và ô hiện #NAME?.

Windows, Mac và Excel trên web

Nền tảngDùng được mã này?Ghi chú
Excel bản máy tính trên WindowsCóCác bước ở trên. Mã không gọi hàm của Windows (không có lệnh Declare) nên không phụ thuộc bản 32 hay 64-bit.
Excel bản máy tính trên MacCó thể, chưa kiểm thửTheo Microsoft, Excel cho Mac hỗ trợ VBA. Trình soạn mở từ Tools > Macro > Visual Basic Editor (phím tắt Windows Alt+F11 không áp dụng nguyên xi). Chúng tôi chưa thử mã trên Mac.
Excel trên webKhôngTheo Microsoft, bạn không thể tạo, chạy hay sửa macro VBA trong Excel trên web (tài liệu). Không có trình soạn VBA ở đó. Dùng công cụ dán cột thay thế.

Mã hoạt động thế nào

Mọi cấu hình dùng cùng một lõi mã đã được kiểm tra. Lựa chọn của bạn chỉ đổi bảng từ (nghìn hay ngàn, linh hay lẻ…) và bốn hằng số Đúng/Sai ở đầu module. Không có hàng chục mẫu viết tay khác nhau. Lõi này là bản chuyển từng bước của engine đang chạy ở trang chủ.

Hàm nhận gì

Số thập phân: chưa hỗ trợ, có chủ đích

Bản VBA hiện chỉ đọc số nguyên. Excel lưu số thập phân dưới dạng nhị phân nên 0,1 có thể không chính xác tuyệt đối, và dấu thập phân phụ thuộc cài đặt vùng. Thay vì đọc sai một chữ số, hàm trả #VALUE!. Ở chế độ số tiền VND điều này khớp với công cụ web: VND không có đơn vị nhỏ hơn đồng, và chúng tôi không tự làm tròn số tiền của bạn. Cần đọc số thập phân thì dùng công cụ trên trang chủ.

Độ chính xác của Excel

Ô số của Excel chỉ giữ 15 chữ số có nghĩa (theo Microsoft). Chữ số vượt quá đã mất ngay khi nhập vào ô số, không hàm nào lấy lại được. Vì vậy hàm không nhận số từ 1E+15 trở lên ở ô số, và không quảng cáo độ chính xác vô hạn. Với số dài, định dạng ô là Text trước khi nhập rồi truyền chuỗi chữ số; ví dụ có dấu ngoặc kép trong bảng “Kiểm tra sau khi cài” dùng cách đó.

Mã được kiểm tra thế nào, và chưa kiểm tra gì

An toàn và minh bạch

VBA là mã chạy được. Chỉ dán và bật macro cho mã bạn đã đọc và hiểu. Đừng chọn “Enable all macros” (bật mọi macro) trong Trust Center: lựa chọn đó áp dụng cho mọi file, kể cả file lạ.

Mã tạo ra ở đây hiển thị đầy đủ, không mã hóa, không rút gọn. Nó không: kết nối mạng, đọc hay ghi file, chạy lệnh hệ thống, gọi hàm của Windows, đọc dữ liệu khác trong sổ làm việc, hay gắn mã theo dõi. Phần đầu mã ghi rõ điều đó.

Tự kiểm tra trong 30 giây: trong trình soạn VBA nhấn Ctrl+F và tìm lần lượt CreateObject, Shell, http, Declare, Open. Mã đúng sẽ không tìm thấy từ nào trong số đó ngoài dòng ghi chú ở đầu.

Nút “Tải xuống mã nguồn (.bas)” chỉ tạo một tệp văn bản chứa đúng đoạn mã trên, ngay trên máy bạn. Đó không phải file Excel. sothanhchu.com không phát hành file .xlsm, .xlam hay .dll.

Lỗi thường gặp

Dấu hiệuNguyên nhân thường gặpCách xử lý
#NAME? ngay sau khi dánMã chưa nằm trong file đang mở, tên hàm gõ sai, hoặc macro chưa được bật.Kiểm tra mã nằm trong Module của đúng file này, gõ lại tên hàm đúng như ở khung mã, bật nội dung của file nếu thanh cảnh báo hiện ra. Còn lỗi: bộ kiểm tra #NAME?.
#NAME? sau khi mở lại fileFile đã bị lưu thành .xlsx nên mất mã.Dán lại mã và lưu thành .xlsm.
#VALUE!Số thập phân, số từ 1E+15 trở lên, văn bản có dấu chấm/phẩy (ví dụ 1.250.000), hoặc nhiều ô.Dùng số nguyên trong ô số, hoặc văn bản chỉ gồm chữ số. Xem “Hàm nhận gì” ở trên.
Thanh cảnh báo bảo mật vàng, macro bị tắtExcel tắt macro hoặc chặn file tải từ internet.Chỉ bật khi bạn đã đọc mã. Không đổi cả hệ thống sang “Enable all macros”. File tải về có thể cần mở khóa theo hướng dẫn của Microsoft.
Báo lỗi “Ambiguous name detected”Cùng một tên hàm xuất hiện hai lần trong file (dán hai lần, hoặc hai module cùng tên).Xóa một bản, hoặc đổi tên hàm ở mục “Đổi tên hàm” cho bản thứ hai.
Báo lỗi biên dịch ngay sau khi dánMã bị biến dạng khi chép (qua chat hoặc ghi chú đổi dấu ngoặc kép), hoặc dán lẫn với mã cũ.Dùng nút “Sao chép code VBA”, dán vào module trống.
Chữ khác với ví dụ (nghìn/ngàn, lẻ/linh…)Cấu hình khác với mã đang dán.Mỗi dòng ở “Kiểm tra sau khi cài” đã tính theo cấu hình hiện tại; tạo lại mã nếu bạn đổi lựa chọn.

Khi nào nên dùng VBA, và khi nào không

VBA hợp khi bạn muốn tự kiểm soát cách đọc, dùng offline và đọc được toàn bộ mã nguồn. Đổi lại: phải bật macro, file phải là .xlsm, người nhận cũng phải bật macro, và Excel trên web hay Mac có khác biệt.

Không có cách nào hợp với mọi trường hợp. Chỉ cần đổi một cột một lần: dán cột vào công cụ, không cần macro. Muốn so sánh các cách (add-in, công thức LET/LAMBDA, Office Add-ins): xem bảng so sánh. Vì sao tên hàm ở đây là DOCSO/DOCVND thay vì VND: xem trang hàm VND.

Câu hỏi thường gặp

Mã này có miễn phí và có gửi dữ liệu đi đâu không?

Miễn phí. Trang tạo mã ngay trong trình duyệt và không gửi lựa chọn của bạn đi. Mã VBA tạo ra không có lệnh mạng nào, nên số liệu trong bảng tính không rời khỏi máy bạn.

Vì sao hàm tên DOCSO/DOCVND mà không phải VND?

Tên VND đã được nhiều add-in và đoạn mã khác nhau dùng với cú pháp khác nhau, nên một file dùng VND() chỉ đúng trên máy có đúng bản đó. Hai tên mặc định ở đây tránh va chạm, và bạn có thể đổi tên. Chi tiết ở trang hàm VND.

DOCSO có thể trùng với hàm của add-in khác không?

Có thể. DOCSO là tên khá phổ biến và chúng tôi không biết máy của bạn đã có gì. Nếu bạn đã cài add-in hoặc mã khác dùng cùng tên, hãy đổi tên hàm ở mục “Đổi tên hàm” (ví dụ ChuSo) để tránh nhầm hai hàm khác nhau. Xem thêm vấn đề trùng tên.

Dùng hàm này trong mọi file được không?

Mã nằm trong file nào thì hàm chỉ có trong file đó. Dùng cho mọi file cần đóng gói thành add-in (.xlam), một việc nâng cao nằm ngoài trang này; sothanhchu.com không phát hành add-in.

Có đọc được số thập phân không?

Chưa. Hàm trả #VALUE! thay vì đọc sai. Xem mục “Số thập phân: chưa hỗ trợ, có chủ đích”.

Đã chạy thử trong Microsoft Excel chưa?

Chưa. Chúng tôi chạy mã bằng LibreOffice ở chế độ tương thích VBA và chưa có Microsoft Excel để thử. Chi tiết ở mục “Mã được kiểm tra thế nào”. Hãy dùng bảng “Kiểm tra sau khi cài” để tự xác nhận trên máy bạn.

Dùng được trên Excel đời cũ không?

Mã chỉ dùng các lệnh VBA cơ bản, nhưng chúng tôi chưa thử trên từng phiên bản Excel nên không cam kết phiên bản cụ thể. Bảng Excel 2016, 2019, 2021, 2024, Microsoft 365 và Excel trên web ở trang Excel cho biết bản nào có VBA.

Công cụ liên quan