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.
Đổ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.
- Số thường
- nghìn, linh, mốt, bốn, lăm
- Viết hoa chữ đầu
Tên hàm chưa hợp lệ nên chưa tạo mã. Sửa tên ở mục “Đổi tên hàm” hoặc dùng tên mặc định.
' ============================================================
' 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.
| Gõ vào ô Excel | Phả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
- Mở file Excel cần dùng hàm (hoặc tạo file mới).
- 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).
- Trong trình soạn, vào Insert > Module. Một cửa sổ trắng hiện ra.
- 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. - Đó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. - 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?.
- Chọn File > Save As, ở ô loại file chọn Excel Macro-Enabled Workbook (*.xlsm). Định dạng nhị phân .xlsb cũng giữ được macro.
- Người nhận file .xlsm phải bật macro thì hàm mới chạy. File tải từ internet có thể bị Excel chặn macro theo mặc định (Microsoft giải thích).
- Chỉ cần gửi kết quả? Chép cột kết quả rồi Paste Special > Values: file không còn phụ thuộc vào mã.
Windows, Mac và Excel trên web
| Nền tảng | Dùng được mã này? | Ghi chú |
|---|---|---|
| Excel bản máy tính trên Windows | Có | 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 Mac | Có 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 web | Không | Theo 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ì
- Một ô chứa số nguyên nhỏ hơn 1E+15, hoặc một ô văn bản chỉ gồm chữ số (có thể có dấu trừ ở đầu, tối đa 36 chữ số).
- Ô trống hoặc văn bản trống cho ra ô trống.
- Mọi thứ khác (số thập phân, văn bản có dấu chấm hoặc phẩy, chữ cái, ngày, đúng/sai, nhiều ô) cho ra
#VALUE!. Lỗi có sẵn trong ô được giữ nguyên.
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ì
- Đã làm: chạy chính đoạn mã này bằng LibreOffice 7.3 ở chế độ tương thích VBA, 28 cấu hình, 7.308 phép so sánh với engine của trang chủ và với một bộ đọc số độc lập, 7.308 kết quả trùng khớp. Có thử cố tình làm hỏng mã để chắc rằng phép so sánh phát hiện được lỗi.
- Chưa làm: chạy trong Microsoft Excel thật. LibreOffice không phải Excel nên không chứng minh mã chạy y hệt trong Excel, đặc biệt ở cách Excel truyền ô vào hàm. Vì vậy bảng “Kiểm tra sau khi cài” là bước xác nhận trên máy của bạn.
- Chưa làm: thử trên Mac và trên từng phiên bản Excel.
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ệu | Nguyên nhân thường gặp | Cách xử lý |
|---|---|---|
#NAME? ngay sau khi dán | Mã 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 file | File đã 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ắt | Excel 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án | Mã 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
- Đổi số thành chữ trong Excel: dán cả cột, không cần macro.
- Hàm VND trong Excel: VND() có sẵn hay do add-in cung cấp.
- Lỗi #NAME? khi dùng hàm VND: bộ kiểm tra từng bước.