Trong một số trường hợp, bạn có một con số (ví dụ: 5787) và cần chuyển nó sang dạng chữ (năm nghìn bảy trăm tám mươi bảy)… Đó là một cách rất thường dùng trong các giấy tờ kế toán, cũng như nhiều trường hợp muốn tránh nhầm lẫn từ người dùng khi sử dụng để đọc số tiền hay một cái gì đó tương tự.
Bài viết này, tôi xin giới thiệu một giải thuật đơn giản làm được việc này, và trong nỗ lực của mình, tôi cũng xin đưa ra một số đoạn code ví dụ để các nhà phát triển sử dụng trong trường hợp cần thiết (xem thêm quy định về giấy phép ở cuối bài này).
Thật ra, nguyên tắc đọc số trong tiếng Việt cũng tương đối phức tạp vì nó có quá nhiều trường hợp “ngoại lệ”, ví dụ như có lúc sử dụng “mươi” rồi “lẻ”… Tuy nhiên, không phải là không thể phân tích ra được (Thực tế là có khá nhiều người xài rồi và được ứng dụng rộng rãi trong các phần mềm xử lý giao dịch).
Các bạn cũng có thể xem demo rõ hơn tại: http://kakalia.co.cc/demo/docso.html
Giải thuật tổng quát
Chúng ta có thể nhận thấy rằng các chữ số thường được chia ra thành nhiều block (tỷ, triệu, nghìn, đơn vị), và người ta sẽ thêm một hậu tố ở sau mỗi block. Mỗi block đó có cách đọc hoàn toàn giống nhau (trăm, chục, đơn vị). Như vậy, cách đọc số là tách số ra thành nhiều block, rồi gọi hàm đọc từng block sau đó thêm hậu tố vào là xong.
Trước hết, khởi tạo mảng các chữ số để sử dụng nhiều lần:
mangso = [ 'không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín' ]
Các hàm bổ trợ của chương trình như sau: (viết bằng mã pseudo-code)
FUNCTION dochangtrieu( so : Integer, daydu : Boolean ) trieu = so DIV 1.000.000 so = so MOD 1.000.000 IF trieu > 0 THEN chuoi = docblock( trieu, daydu ) + " trieu" daydu = TRUE ENDIF nghin = so DIV 1.000 so = so MOD 1.000 IF nghin > 0 THEN chuoi += docblock( nghin, daydu ) + " nghìn" daydu = TRUE ENDIF IF so > 0 THEN chuoi += docblock( so, daydu ) ENDIF RETURN chuoi END. FUNCTION docblock( so : Integer, daydu : Boolean ) tram = so DIV 100 so = so MOD 100 IF daydu OR tram > 0 THEN chuoi = " " + mangso[ tram ] + " trăm" chuoi += dochangchuc(so, TRUE) ELSE chuoi = dochangchuc(so, FALSE) RETURN chuoi END. FUNCTION dochangchuc(so : Integer, daydu : Boolean) chuc = so DIV 10 donvi = so MOD 10 IF chuc > 1 THEN chuoi = " " + mangso[ chuc ] + " mươi" IF donvi = 1 THEN chuoi += " mốt" ENDIF ELSE IF chuc = 1 THEN chuoi = " mười" IF donvi = 1 THEN chuoi += " một" ENDIF ELSE IF daydu AND donvi > 0 THEN chuoi = " lẻ" ENDIF IF donvi = 5 AND chuc > 1 THEN chuoi += " lăm" ELSE IF donvi > 1 OR (donvi = 1 AND chuc = 0) THEN chuoi += " " + mangso[ donvi ] ENDIF RETURN chuoi END.
Bây giờ, ta sẽ viết hàm đọc số chính:
FUNCTION docso( so : Integer ) IF so = 0 THEN RETURN mangso[ 0 ]; ENDIF REPEAT ty = so MOD 1.000.000.000 so = so DIV 1.000.000.000 IF so > 0 THEN chuoi = dochangtrieu( ty, TRUE ) + hauto + chuoi ELSE chuoi = dochangtrieu( ty, FALSE) + hauto + chuoi ENDIF hauto = " tỷ" UNTIL so <= 0 RETURN chuoi END.
OK, với đoạn mã giả này thì bạn có thể viết lại bằng bất cứ ngôn ngữ nào. Dưới đây, tôi sẽ viết vài loại ngôn ngữ thông dụng mà tôi biết, nếu bạn có thể viết trong ngôn ngữ khác thì hãy gửi lại cho tôi để tôi chia sẻ cho mọi người.
Mã Javascript
Đoạn mã bên dưới chạy trên tất cả các trình duyệt.
var mangso = ['không','một','hai','ba','bốn','năm','sáu','bảy','tám','chín']; function dochangchuc(so,daydu) { var chuoi = ""; chuc = Math.floor(so/10); donvi = so%10; if (chuc>1) { chuoi = " " + mangso[chuc] + " mươi"; if (donvi==1) { chuoi += " mốt"; } } else if (chuc==1) { chuoi = " mười"; if (donvi==1) { chuoi += " một"; } } else if (daydu && donvi>0) { chuoi = " lẻ"; } if (donvi==5 && chuc>1) { chuoi += " lăm"; } else if (donvi>1||(donvi==1&&chuc==0)) { chuoi += " " + mangso[ donvi ]; } return chuoi; } function docblock(so,daydu) { var chuoi = ""; tram = Math.floor(so/100); so = so%100; if (daydu || tram>0) { chuoi = " " + mangso[tram] + " trăm"; chuoi += dochangchuc(so,true); } else { chuoi = dochangchuc(so,false); } return chuoi; } function dochangtrieu(so,daydu) { var chuoi = ""; trieu = Math.floor(so/1000000); so = so%1000000; if (trieu>0) { chuoi = docblock(trieu,daydu) + " triệu"; daydu = true; } nghin = Math.floor(so/1000); so = so%1000; if (nghin>0) { chuoi += docblock(nghin,daydu) + " nghìn"; daydu = true; } if (so>0) { chuoi += docblock(so,daydu); } return chuoi; } function docso(so) { if (so==0) return mangso[0]; var chuoi = "", hauto = ""; do { ty = so%1000000000; so = Math.floor(so/1000000000); if (so>0) { chuoi = dochangtrieu(ty,true) + hauto + chuoi; } else { chuoi = dochangtrieu(ty,false) + hauto + chuoi; } hauto = " tỷ"; } while (so>0); return chuoi; }
Mã PHP
Đoạn mã bên dưới chỉ chạy với PHP 4 trở lên. Nhưng đừng lo lắng vì chuyện đó, vì hầu hết các máy chủ PHP đều hỗ trợ chuẩn PHP5 mà, chạy rất tốt.
$mangso = array('không','một','hai','ba','bốn','năm','sáu','bảy','tám','chín'); function dochangchuc($so,$daydu) { global $mangso; $chuoi = ""; $chuc = floor($so/10); $donvi = $so%10; if ($chuc>1) { $chuoi = " " . $mangso[$chuc] . " mươi"; if ($donvi==1) { $chuoi .= " mốt"; } } else if ($chuc==1) { $chuoi = " mười"; if ($donvi==1) { $chuoi .= " một"; } } else if ($daydu && $donvi>0) { $chuoi = " lẻ"; } if ($donvi==5 && $chuc>1) { $chuoi .= " lăm"; } else if ($donvi>1||($donvi==1&&chuc==0)) { $chuoi .= " " . $mangso[$donvi]; } return $chuoi; } function docblock($so,$daydu) { global $mangso; $chuoi = ""; $tram = floor($so/100); $so = $so%100; if ($daydu || $tram>0) { $chuoi = " " . $mangso[$tram] . " trăm"; $chuoi .= dochangchuc($so,true); } else { $chuoi = dochangchuc($so,false); } return $chuoi; } function dochangtrieu($so,$daydu) { $chuoi = ""; $trieu = floor($so/1000000); $so = $so%1000000; if ($trieu>0) { $chuoi = docblock($trieu,$daydu) . " triệu"; $daydu = true; } $nghin = floor($so/1000); $so = $so%1000; if ($nghin>0) { $chuoi .= docblock($nghin,$daydu) . " nghìn"; $daydu = true; } if ($so>0) { $chuoi .= docblock($so,$daydu); } return $chuoi; } function docso($so) { global $mangso; if ($so==0) return $mangso[0]; $chuoi = ""; $hauto = ""; do { $ty = $so%1000000000; $so = floor($so/1000000000); if ($so>0) { $chuoi = dochangtrieu($ty,true) . $hauto . $chuoi; } else { $chuoi = dochangtrieu($ty,false) . $hauto . $chuoi; } $hauto = " tỷ"; } while ($so>0); return $chuoi; }
Mã C/C++
Giờ là một đoạn code chạy trên C/C++:
string mangso[10] = {'không','một','hai','ba','bốn','năm','sáu','bảy','tám','chín'}; string dochangchuc(int so, bool daydu) { string chuoi = ""; int chuc = so/10; int donvi = so%10; if (chuc>1) { chuoi = " " + mangso[chuc] + " mươi"; if (donvi==1) { chuoi += " mốt"; } } else if (chuc==1) { chuoi = " mười"; if (donvi==1) { chuoi += " một"; } } else if (daydu && donvi>0) { chuoi = " lẻ"; } if (donvi==5 && chuc>1) { chuoi += " lăm"; } else if (donvi>1||(donvi=1&&chuc==0)) { chuoi += " " + mangso[donvi]; } return chuoi; } string docblock(int so, bool daydu) { string chuoi = ""; int tram = so/100; so = so%100; if (daydu || tram>0) { chuoi = " " + mangso[tram] + " trăm"; chuoi += dochangchuc(so,true); } else { chuoi = dochangchuc(so,false); } return chuoi; } string dochangtrieu(int so, bool daydu) { string chuoi = ""; int trieu = so/1000000; so = so%1000000; if (trieu>0) { chuoi = docblock(trieu,daydu) + " triệu"; daydu = true; } int nghin = so/1000; so = so%1000; if (nghin>0) { chuoi += docblock(nghin,daydu) + " nghìn"; daydu = true; } if (so>0) { chuoi += docblock(so,daydu); } return chuoi; } string docso(int so) { if (so==0) { return mangso[0]; } string chuoi = ""; string hauto = ""; int ty=0; do { ty = so%1000000000; so = so/1000000000; if (so>0) { chuoi = dochangtrieu(ty,true) + hauto + chuoi; } else { chuoi = dochangtrieu(ty,false) + hauto + chuoi; } hauto = " tỷ"; } while (so>0); return chuoi; }
Các bạn cũng có thể xem demo rõ hơn tại: http://kakalia.co.cc/demo/docso.html
—————
Tất cả các đoạn mã (mã chạy được và mã giả) trong bài viết này được đặt dưới giấy phép Creative Commons – Attribution 3.0 Unported, phần còn lại (không phải mã nguồn) do tác giả giữ bản quyền.
Thank you so much !
ReplyDeleteBài viết rất hữu ích. Cảm ơn bạn đã chia sẻ.
ReplyDeleteHàm này của bạn không đọc số thực à
ReplyDeletethêm mấy dòng mã nữa để đọc sau dấu phảy nhé
ReplyDeletefunction docso(so){
if (so==0) return mangso[0];
var chuoi = "", hauto = "", tiento = ' ', phancach = ' ';
var list = [];
if((so+'').split('.').length>1){
phancach = ' phảy';
return convert_number_to_words((so+'').split('.')[0]) + phancach + convert_number_to_words((so+'').split('.')[1]);
}else if((so+'').split('-').length>1){
tiento = 'Âm';
return tiento + convert_number_to_words((so+'').split('-')[1]);
}else{
do {
ty = so%1000000000;
so = Math.floor(so/1000000000);
if (so>0) {
chuoi = dochangtrieu(ty,true) + hauto + chuoi;
} else {
chuoi = dochangtrieu(ty,false) + hauto + chuoi;
}
hauto = " tỷ";
} while (so>0);
return chuoi;
}
return chuoi;
}
vẫn còn lối bạn ơi:
ReplyDelete361111 (bằng chữ: ba trăm sáu mươi mốt một nghìn một trăm
mười một một đồng)