Thursday, June 28, 2007

Hàm đọc số thành chuỗi

Ưu điểm: + Đọc được tới số 79.228.162.514.264.337.593.543.950.335 (bảy mươi chín tỉ tỉ tỉ hai trăm hai mươi tám triệu tỉ tỉ một trăm sáu mươi hai ngàn tỉ tỉ năm trăm mười bốn tỉ tỉ hai trăm sáu mươi bốn triệu tỉ ba trăm bốn mươi ngàn tỉ sáu trăm tám mươi mốt tỉ năm trăm bốn mươi lăm triệu bảy trăm bốn mươi bốn ngàn ba trăm tám mươi bốn) + Hỗ trợ các luật phát âm lẩm cẩm: một/mốt, mươi/mười, năm/lăm/nhăm + Cho thay đổi các cách phát âm: linh/lẻ, nghìn/ngàn, vv... Nhược điểm: + Không hỗ trợ số thập phân (vì dùng lại class này 2 lần sẽ đạt được tính năng đó) + Đối với số cực lớn, đôi lúc đọc lệch đi 1 tí xíu (vì bản chất là dựa trên kiểu double, mà bản thân double có precision không cao như decimal) Mã nguồn C#: (Các fan VB.NET chịu khó tìm converter để convert đi nhá, có đầy trên mạng rồi. Với lại tui ko thích chơi với VB) Big Smile [:D] #region Using directives using System; using System.Text; #endregion namespace Thn.Application.Utilities { /// <summary> /// Converts from a number to words. (Vietnamese rule) /// Author : Nguyễn Minh Hải /// Rev : Aug 21, 2006 /// Features : /// + number can be as big/small as a decimal can support /// + does not support decimal points (he he, YOU should implement that) /// Copyright: /// + use it anyway you like, as long as you retain credits to original author /// </summary> public class NumberToText { #region Names /// <summary> /// name of simple number digit /// </summary> protected string[] mDigits = new string[] { "không", "một", "hai", "ba", "bốn", "năm", "sáu", "bảy", "tám", "chín", "mười" }; /// <summary> /// Name of digit groups /// </summary> protected string[] mGroups = new string[] {"trăm", "ngàn", "triệu", "tỉ", "ngàn tỉ", "triệu tỉ", "tỉ tỉ", "ngàn tỉ tỉ", "triệu tỉ tỉ", "tỉ tỉ tỉ" }; /// <summary> /// name of single sequence digit (linh, lẻ) /// </summary> protected string mZeroSequence = "lẻ"; /// <summary> /// alternative name of One /// </summary> protected string mAltOne = "mốt"; /// <summary> /// alternative name of Five /// </summary> protected string mAltFive = "lăm"; /// <summary> /// postfix for tens /// </summary> protected string mTenPostfix = "mươi"; /// <summary> /// name of negative sign /// </summary> protected string mNegative = "trừ"; #region Unit Name private string mUnitName = "đồng"; /// <summary> /// Gets/Sets name of unit /// </summary> public string UnitName { get { return mUnitName; } set { mUnitName = value; } } #endregion #endregion #region Build group bool useZeroHundred = false; /// <summary> /// Build name of a group (expected value: [0, 999] /// </summary> protected virtual string BuildGroup(int value) { StringBuilder result = new StringBuilder(); bool useAlt = false; bool zeroTen = false; bool zeroHundred = false; //build hundred int digit = value / 100; if (useZeroHundred || (digit != 0)) { result.Append(mDigits[digit] + " " + mGroups[0]); } zeroHundred = digit == 0; //build ten digit = value % 100 / 10; if (digit == 0) zeroTen = true; else { if (digit == 1) result.Append(" " + mDigits[10]); else result.Append(" " + mDigits[digit] + " " + mTenPostfix); } useAlt = (digit != 0) && (digit != 1); //build unit digit = value % 10; if ((digit!=0)&& zeroTen) { if (zeroHundred) { if (useZeroHundred) result.Append(" " + mZeroSequence + " " + mDigits[digit]); else result.Append(" " + mDigits[digit]); } else { result.Append(" " + mZeroSequence + " " + mDigits[digit]); } } else if (digit == 1) { if (useAlt) result.Append(" " + mAltOne); else result.Append(" " + mDigits[1]); } else if (digit == 5) { if (zeroTen) result.Append(" " + mDigits[5]); else result.Append(" " + mAltFive); } else if (digit != 0) result.Append(" " + mDigits[digit]); return result.ToString(); } #endregion #region Get Group Name /// <summary> /// Build group's name based on the power of group /// </summary> string GetGroupName(int power) { string result = ""; int idx = power / 3; if ((idx > 0) && (idx < mGroups.Length)) result = mGroups[idx]; //else if (idx > 0) result = GetGroupName(idx) + " " + mGroups[3]; return result; } #endregion #region Parse /// <summary> /// Converts a number to words /// </summary> public virtual string Parse(double value) { string result = ""; StringBuilder sb = new StringBuilder(); if (value == 0) result = mDigits[0] + " " + mUnitName; else { if (value < 0) { result = mNegative + " "; value = -value; } int length = (int)Math.Log10((double)value); int group = 0; useZeroHundred = false; while (length >= 0) { int groupLength = length; while (groupLength % 3 != 0) groupLength--; group = (int)(value / Math.Pow(10, groupLength)); if (group > 0) { sb.Append(BuildGroup(group)); sb.Append(" " + GetGroupName(length) + " "); } string tmp = sb.ToString(); //prepare for next group value = value - group * Math.Pow(10, groupLength); length -= 3; useZeroHundred = true; }//while length > 0 result += sb.ToString().Trim() + " " + mUnitName; } return result; } /// <summary> /// Converts a number to words /// </summary> public virtual string Parse(decimal value) { return Parse((double)value); } /// <summary> /// Converts a number to words /// </summary> public virtual string Parse(int value) { return Parse(value); } #endregion #region Constructors /// <summary> /// Default constructor /// </summary> public NumberToText() { } #endregion } } Danh sách các số đã kiểm chứng: 0 - không đồng 1 - một đồng 2 - hai đồng 3 - ba đồng 4 - bốn đồng 5 - năm đồng 6 - sáu đồng 7 - bảy đồng 8 - tám đồng 9 - chín đồng 10 - mười đồng 11 - mười một đồng 15 - mười lăm đồng 12 - mười hai đồng 20 - hai mươi đồng 21 - hai mươi mốt đồng 22 - hai mươi hai đồng 25 - hai mươi lăm đồng 99 - chín mươi chín đồng -99 - trừ chín mươi chín đồng 100 - một trăm đồng 101 - một trăm lẻ một đồng 102 - một trăm lẻ hai đồng 105 - một trăm lẻ năm đồng 110 - một trăm mười đồng 111 - một trăm mười một đồng 112 - một trăm mười hai đồng 115 - một trăm mười lăm đồng 201 - hai trăm lẻ một đồng 202 - hai trăm lẻ hai đồng 205 - hai trăm lẻ năm đồng 210 - hai trăm mười đồng 211 - hai trăm mười một đồng 212 - hai trăm mười hai đồng 215 - hai trăm mười lăm đồng 220 - hai trăm hai mươi đồng 221 - hai trăm hai mươi mốt đồng 222 - hai trăm hai mươi hai đồng 225 - hai trăm hai mươi lăm đồng 500 - năm trăm đồng 505 - năm trăm lẻ năm đồng 550 - năm trăm năm mươi đồng 555 - năm trăm năm mươi lăm đồng 995 - chín trăm chín mươi lăm đồng 999 - chín trăm chín mươi chín đồng 1.000 - một ngàn đồng 1.001 - một ngàn không trăm lẻ một đồng 1.005 - một ngàn không trăm lẻ năm đồng 1.009 - một ngàn không trăm lẻ chín đồng 1.010 - một ngàn không trăm mười đồng 1.011 - một ngàn không trăm mười một đồng 1.012 - một ngàn không trăm mười hai đồng 1.015 - một ngàn không trăm mười lăm đồng 1.020 - một ngàn không trăm hai mươi đồng 1.021 - một ngàn không trăm hai mươi mốt đồng 1.022 - một ngàn không trăm hai mươi hai đồng 1.025 - một ngàn không trăm hai mươi lăm đồng 1.100 - một ngàn một trăm đồng 1.101 - một ngàn một trăm lẻ một đồng 1.102 - một ngàn một trăm lẻ hai đồng 1.105 - một ngàn một trăm lẻ năm đồng 1.110 - một ngàn một trăm mười đồng 1.111 - một ngàn một trăm mười một đồng 1.115 - một ngàn một trăm mười lăm đồng 1.200 - một ngàn hai trăm đồng 1.201 - một ngàn hai trăm lẻ một đồng 1.205 - một ngàn hai trăm lẻ năm đồng 1.210 - một ngàn hai trăm mười đồng 1.211 - một ngàn hai trăm mười một đồng 1.215 - một ngàn hai trăm mười lăm đồng 1.500 - một ngàn năm trăm đồng 1.505 - một ngàn năm trăm lẻ năm đồng 1.550 - một ngàn năm trăm năm mươi đồng 1.555 - một ngàn năm trăm năm mươi lăm đồng 1.000.000 - một triệu đồng 1.000.001 - một triệu không trăm lẻ một đồng 1.000.002 - một triệu không trăm lẻ hai đồng 1.000.010 - một triệu không trăm mười đồng 1.000.011 - một triệu không trăm mười một đồng 1.000.100 - một triệu một trăm đồng 1.000.101 - một triệu một trăm lẻ một đồng 1.001.000 - một triệu không trăm lẻ một ngàn đồng 1.001.001 - một triệu không trăm lẻ một ngàn không trăm lẻ một đồng 1.010.000 - một triệu không trăm mười ngàn đồng 1.011.000 - một triệu không trăm mười một ngàn đồng 1.011.001 - một triệu không trăm mười một ngàn không trăm lẻ một đồng 1.100.000 - một triệu một trăm ngàn đồng 1.110.000 - một triệu một trăm mười ngàn đồng 1.200.000 - một triệu hai trăm ngàn đồng 1.210.000 - một triệu hai trăm mười ngàn đồng 10.000.000 - mười triệu đồng 11.000.000 - mười một triệu đồng 100.000.000 - một trăm triệu đồng 200.000.000 - hai trăm triệu đồng 1.000.000.000 - một tỉ đồng 10.000.000.000 - mười tỉ đồng 100.000.000.000 - một trăm tỉ đồng 1.000.000.000.000 - một ngàn tỉ đồng 10.000.000.000.000 - mười ngàn tỉ đồng 100.000.000.000.000 - một trăm ngàn tỉ đồng 1.000.000.000.000.000 - một triệu tỉ đồng 10.000.000.000.000.000 - mười triệu tỉ đồng 100.000.000.000.000.000 - một trăm triệu tỉ đồng 1.000.000.000.000.000.000 - một tỉ tỉ đồng 10.000.000.000.000.000.000 - mười tỉ tỉ đồng 100.000.000.000.000.000.000 - một trăm tỉ tỉ đồng 1.000.000.000.000.000.000.000 - một ngàn tỉ tỉ đồng 10.000.000.000.000.000.000.000 - mười ngàn tỉ tỉ đồng 100.000.000.000.000.000.000.000 - chín mươi chín ngàn tỉ tỉ chín trăm chín mươi chín tỉ tỉ chín trăm chín mươi chín triệu tỉ chín trăm chín mươi chín ngàn tỉ chín trăm chín mươi chín tỉ chín trăm tám mươi chín triệu năm trăm mười bốn ngàn hai trăm bốn mươi đồng 1.000.000.000.000.000.000.000.000 - một triệu tỉ tỉ đồng 10.000.000.000.000.000.000.000.000 - mười triệu tỉ tỉ không trăm lẻ hai tỉ một trăm bốn mươi bảy triệu bốn trăm tám mươi ba ngàn sáu trăm bốn mươi tám đồng 100.000.000.000.000.000.000.000.000 - một trăm triệu tỉ tỉ đồng 1.000.000.000.000.000.000.000.000.000 - một tỉ tỉ tỉ đồng 10.000.000.000.000.000.000.000.000.000 - mười tỉ tỉ tỉ đồng 79.228.162.514.264.337.593.543.950.335 - bảy mươi chín tỉ tỉ tỉ hai trăm hai mươi tám triệu tỉ tỉ một trăm sáu mươi hai ngàn tỉ tỉ năm trăm mười bốn tỉ tỉ hai trăm sáu mươi bốn triệu tỉ ba trăm bốn mươi ngàn tỉ sáu trăm tám mươi mốt tỉ năm trăm bốn mươi lăm triệu bảy trăm bốn mươi bốn ngàn ba trăm tám mươi bốn đồng Viết bằng ngôn ngữ VB.NET Option Explicit On Imports System.Text Public Class ReadNumVNUnicode '********************************^^^^^^^************************************* ' All things I've done here is for education and research purposes only!!! ' Mail: classical_boy1988@yahoo.com '**************************************************************************** #Region "First Declare & Global Variants..." Public Event NotNumeric(ByVal strWrongNum As String) ' biến cố dùng cho ng</FONT><FONT size=2>ýời dùng nếu không phải là số... 'Các biến lýu giữ tính chất... Dim mReadZero As String = "lẻ" Dim mReadDecimal As String = "phẩy" Dim mReadNegative As String = "trừ" Dim mReadThousand As String = "ngh</FONT><FONT size=2>ìn" Dim mReadDigit5 As String = "l</FONT><FONT size=2>ăm" ' Private Variants... Dim strDecimal As String 'biến to</FONT><FONT size=2>àn cục, chứa thông tin về dấu chấm thập phân Dim strThousand As String 'biến toàn cục, chứa thông tin về dấu phân cách hàng ngàn Dim iDecimal As Int32 ' biến toàn cục, giữ vị trí của dấu phẩy trong số </FONT><FONT size=2>được đọc. Dim bNegative As Boolean ' số được đọc âm hay dương? 'Các mảng lưu cách đọc... Dim arrHangDV() As String = {"", "mốt ", "hai ", "ba ", _ "bốn ", "lăm ", "sáu ", "bảy ", "tám ", "chín "} Dim arrHangDV0() As String = {"không ", "một ", "hai ", "ba ", _ "bốn ", "năm ", "sáu ", "bảy ", "tám ", "chín "} Dim arrHangDV1() As String = {"", "một ", "hai ", "ba ", _ "bốn ", "lăm ", "sáu ", "bảy ", "tám ", "chín "} Dim arrHangChuc() As String = {"lẻ ", "mười ", "hai mươi ", "ba mươi ", _ "bốn mươi ", "năm mươi ", "sáu mươi ", "bảy mươi ", "tám mươi ", "chín mươi "} Dim arrHangTram() As String = {"không trăm ", "một trăm ", "hai trăm ", "ba trăm ", _ "bốn trăm ", "năm trăm ", "sáu trăm ", "bảy trăm ", "tám trăm ", "chín trăm "} #End Region #Region "Properties " Public Property ReadThousand() As String 'Cách đọc h</FONT><FONT size=2>àng nghìn, vdụ: nghìn, ngàn... Mặc </FONT><FONT size=2>định l</FONT><FONT size=2>à "nghìn" Get Return mReadThousand End Get Set(ByVal value As String) mReadZero = value End Set End Property Public Property ReadDigit5() As String 'Cách đọc số 5, vdụ: lăm, nhăm...(hai mươi lăm, hai mươi nhăm). Mặc định là "lăm" 'Lưu ý: 105 đọc là "một trăm lẻ năm", ko thể đọc là "một trăm lẻ nhăm" hay "một trăm lẻ lăm" Get Return mReadDigit5 End Get Set(ByVal value As String) mReadDigit5 = value arrHangDV(5) = mReadDigit5 & Chr(32) arrHangDV1(5) = mReadDigit5 & Chr(32) End Set End Property Public Property ReadZero() As String 'Cách đọc số không (0) ở hàng chục, vdụ: lẻ, linh... Mặc định là "lẻ" Get Return mReadZero End Get Set(ByVal value As String) mReadZero = value arrHangChuc(0) = Trim(mReadZero) & Chr(32) End Set End Property Public Property ReadNegative() As String 'Cách đọc dấu trừ nếu là số âm, vdụ: trừ, âm... Mặc </FONT><FONT size=2>định l</FONT><FONT size=2>à "trừ" Get Return mReadNegative End Get Set(ByVal value As String) mReadNegative = value End Set End Property Public Property ReadDecimal() As String 'Cách </FONT><FONT size=2>đọc dấu chấm thập phân, vdụ: phết, phẩy... Mặc định l</FONT><FONT size=2>à "phẩy" Get Return mReadDecimal End Get Set(ByVal value As String) mReadDecimal = value End Set End Property Public ReadOnly Property DecimalSymbol() As String 'Cho biết kí hiệu dùng </FONT><FONT size=2>để biểu diễn dấu chấm thập phân cảu hệ thống Get DecimalSymbol = strDecimal End Get End Property Public ReadOnly Property DigitGroupSymbol() As String 'Kí hiệu d</FONT><FONT size=2>ùng </FONT><FONT size=2>để biểu diễn dấu phân cách h</FONT><FONT size=2>àng ngàn của hệ thống... Get DigitGroupSymbol = strThousand End Get End Property #End Region #Region "Private Subs " Private Function GetDicemal(ByRef a As Byte) As String ' lấy thông tin về dấu chấm thập phân & dấu phân cách hàng ngàn Dim regKey As Microsoft.Win32.RegistryKey regKey = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Control PanelInternational") If a = 0 Then ' lấy thông tin về dấu chấm thập phân GetDicemal = regKey.GetValue("sDecimal", ",").ToString Else 'lấy thông tin về dấu phân cách hàng ngàn GetDicemal = regKey.GetValue("sThousand", ".").ToString End If regKey = Nothing End Function Private Function Read3Digits(ByVal str3Digits As String, ByRef k As Int32) As String Dim sRead As String = "" Dim iDV, iChuc, iTram As Int32 If str3Digits = "000" Then Return vbNullString iDV = CInt(Right(str3Digits, 1)) If str3Digits.Length > 1 Then iChuc = CInt(Mid(str3Digits, str3Digits.Length - 1, 1)) iTram = IIf(str3Digits.Length = 3, CInt(Left(str3Digits, 1)), 0) If str3Digits.Length = 1 Then sRead = arrHangDV0(iDV) If str3Digits.Length = 2 Then sRead = arrHangChuc(iChuc) & Choose(IIf(iChuc > 1, 3, iChuc + 1), _ arrHangDV0(iDV), arrHangDV1(iDV), arrHangDV(iDV)) If str3Digits.Length = 3 Then sRead = arrHangTram(iTram) & IIf(iChuc + iDV <> 0, _ Read3Digits(iChuc.ToString & iDV.ToString, 1), "") 'Trên thực tế, bạn có thể thay arrHangTram(iTram) bằng arrHangDV0(iTram) & "tr</FONT><FONT size=2>ăm " sRead = sRead & Choose(k Mod 3 + 1, "triệu ", "", mReadThousand & Chr(32)) & _ Space((k - 1) 3).Replace(Chr(32), "tỉ ") Return sRead End Function Private Function CheckNum(ByRef sNumToCheck As String) As String 'H</FONT><FONT size=2>àm kiểm tra lại số mà ng</FONT><FONT size=2>ýời dùng nhập Dim i As Int32 = 1, num As String = sNumToCheck Do While i <= num.Length Select Case Asc(Mid(num, i, 1)) Case Asc("0") To Asc("9") ' không làm g</FONT><FONT size=2>ì cả!!! Case Asc(Trim(strDecimal)) If iDecimal <> 0 Then GoTo NotNum Else If i = 1 Then num = "0" & num iDecimal = i End If Case Asc("-") If bNegative Or i <> 1 Then GoTo NotNum Else bNegative = True End If Case Else NotNum: num = Left(num, i - 1) & Right(num, Len(num) - i) i = i - 1 End Select i = i + 1 Loop If Not (IsNumeric(num)) Then RaiseEvent NotNumeric(sNumToCheck) Return vbNullString Else If num.Replace("0", "") = "" Then num = "0" Else i = 0 Do While i < num.Length If Left(num, 1) = "0" Then num = Mid(num, 2) Else Exit Do End If i += 1 Loop End If 'Return FormatNumber(num, IIf(iDecimal <> 0, num.Length - iDecimal, 0), TriState.True, TriState.False, TriState.False) Return num End If End Function #End Region Public Function Read(ByVal Number As String) As StringBuilder Dim i, iStart, iLen As Int32 Dim sNum As String, sRead As New StringBuilder("") sNum = Number sNum = CheckNum(Number) If Trim(sNum) = vbNullString Then Return sRead If bNegative Then ' nếu là số âm sRead.Append(mReadNegative & Chr(32)) sNum = Mid(sNum, 2) : bNegative = False End If If iDecimal <> 0 Then ' nếu là số thập phân iStart = iDecimal - IIf(sRead.Length <> 0, 1, 0) 'Giải thích: sRead.Length <> 0 nghĩa là </FONT><FONT size=2>đ</FONT><FONT size=2>ã </FONT><FONT size=2>đọc mReadNegative v</FONT><FONT size=2>ào rồi (số âm) iDecimal = 0 sRead.Append(Read(Left(sNum, iStart - 1)).ToString & mReadDecimal & _ Chr(32) & Read(Mid(sNum, iStart + 1)).ToString) Else 'nếu là số nguyên (ko chấm thập phân) iStart = 1 iLen = IIf(sNum.Length Mod 3 = 0, 3, sNum.Length - 3 * (sNum.Length 3)) For i = sNum.Length 3 + Math.Sign(sNum.Length Mod 3) To 1 Step -1 sRead.Append(Read3Digits(Mid(sNum, iStart, iLen), i)) iStart += iLen : iLen = 3 Next End If Return sRead End Function Public Sub New() strDecimal = GetDicemal(0) 'lay thong tin ve cham thap phan strThousand = GetDicemal(1) ' lay thong tin ve dau cach hang ngan End Sub End Class

No comments:

Post a Comment