財務系統、發票列印、支票列印常需將阿拉伯數字金額轉為中文大寫 (「壹佰貳拾叁元整」)。本文提供完整的 DecimalToChineseCurrency 函數,支援整數、小數 (角/分)、大數值 (萬/億/兆)、零值壓縮 (「零零」→「零」)、單位重疊修正 (「零億」→「億」)。
function DecimalToChineseCurrency(const Decimal: string): string;
var
NumMap, UnitMap: TStringList;
I, DotPos, UnitIdx: Integer;
CleanStr: string;
HasDecimal: Boolean;
begin
Result := Decimal; // 預設回傳原值 (格式錯誤時)
HasDecimal := False;
DotPos := 0;
// 1. 輸入驗證:僅允許數字與單一小數點
for I := 1 to Length(Decimal) do
begin
if Decimal[I] = '.' then
begin
if HasDecimal then Exit; // 多個小數點
HasDecimal := True;
DotPos := I;
end
else if not (Decimal[I] in ['0'..'9']) then
Exit; // 非法字元
end;
// 2. 統一處理:補齊小數點位置
CleanStr := Decimal;
if not HasDecimal then
begin
CleanStr := CleanStr + '.';
DotPos := Length(CleanStr);
end;
// 移除小數點,純數字字串
CleanStr := StringReplace(CleanStr, '.', '', [rfReplaceAll]);
// 3. 數字與單位映射表
NumMap := TStringList.Create;
UnitMap := TStringList.Create;
try
NumMap.Delimiter := ',';
UnitMap.Delimiter := ',';
// 數字映射:0=零, 1=壹, 2=貳, 3=叁, 4=肆, 5=伍, 6=陸, 7=柒, 8=捌, 9=玖
NumMap.CommaText := '0=零,1=壹,2=貳,3=叁,4=肆,5=伍,6=陸,7=柒,8=捌,9=玖';
// 單位映射:從個位開始 (索引對應位數)
// -1=分, 0=角, 1=元, 2=拾, 3=佰, 4=仟, 5=萬, 6=拾, 7=佰, 8=仟, 9=億, 10=拾, 11=佰, 12=仟, 13=兆...
UnitMap.CommaText :=
'-1=分,0=角,1=元,2=拾,3=佰,4=仟,5=萬,6=拾,7=佰,8=仟,9=億,10=拾,11=佰,12=仟,13=兆,14=拾,15=佰,16=仟';
// 4. 從低位向高位處理 (個位索引 1 對應 CleanStr 最後一字元)
Result := '';
for I := 1 to Length(CleanStr) do
begin
var Digit := CleanStr[Length(CleanStr) - I + 1]; // 取對應數字字元
UnitIdx := I - DotPos; // 單位索引:個位=1, 十位=2... 小數點左側為正
if Digit = '0' then
begin
// 零處理:四捨五入單位 (萬/億/兆/元) 保留單位,其他省略
if (UnitIdx mod 4 = 1) then // 元、萬、億、兆 位
Result := UnitMap.Values[IntToStr(UnitIdx)] + Result
else
Result := NumMap.Values['0'] + Result; // 普通零
end
else
begin
// 非零:數字 + 單位
Result := NumMap.Values[Digit] + UnitMap.Values[IntToStr(UnitIdx)] + Result;
end;
end;
// 5. 後處理:零值壓縮與單位修正
// 連續零合併
while Pos('零零', Result) > 0 do
Result := StringReplace(Result, '零零', '零', [rfReplaceAll]);
// 高階單位前的零移除
Result := StringReplace(Result, '零兆', '兆', [rfReplaceAll]);
Result := StringReplace(Result, '零億', '億', [rfReplaceAll]);
Result := StringReplace(Result, '零萬', '萬', [rfReplaceAll]);
Result := StringReplace(Result, '零元', '元', [rfReplaceAll]);
// 單位重疊修正 (如 萬億 → 億)
Result := StringReplace(Result, '兆億', '兆', [rfReplaceAll]);
Result := StringReplace(Result, '億萬', '億', [rfReplaceAll]);
Result := StringReplace(Result, '兆萬', '兆', [rfReplaceAll]);
// 結尾處理
if Result.EndsWith('零') then
Result := Result.Substring(0, Result.Length - 1); // 移除末尾零
if Result.EndsWith('分') or Result.EndsWith('角') then
Result := Result + '零'; // 如 "壹角" → "壹角零" (習慣寫法)
finally
NumMap.Free;
UnitMap.Free;
end;
end;
function ChineseCurrencyToDecimal(const ChineseCurrency: string): string;
var
Map: TStringList;
I, PairCount: Integer;
Pair: string;
begin
Map := TStringList.Create;
try
Map.Delimiter := ',';
Map.CommaText := '元=.,零=0,壹=1,貳=2,叁=3,肆=4,伍=5,陸=6,柒=7,捌=8,玖=9';
Result := '';
PairCount := Length(ChineseCurrency) div 2; // 假設格式為 "數字單位" 成對
for I := PairCount downto 1 do
begin
Pair := ChineseCurrency[(I-1)*2 + 1] + ChineseCurrency[I*2]; // 取一對
if Map.Values[Pair] <> '' then
Result := Map.Values[Pair] + Result;
end;
// 簡易處理:將中文單位轉為小數點位置
// 完整實作需解析單位位權,此處略
finally
Map.Free;
end;
end;
procedure TestCurrencyConversion;
var
Tests: array of record
Input, Expected: string;
end;
I: Integer;
begin
Tests := [
('0', '零元'),
('1', '壹元'),
('10', '壹拾元'),
('100', '壹佰元'),
('1000', '壹仟元'),
('10000', '壹萬元'),
('100000000', '壹億元'),
('123.45', '壹佰貳拾叁元肆角伍分'),
('1001.00', '壹仟零壹元'),
('1000100', '壹佰萬零壹佰元'),
('0.01', '壹分'),
('0.10', '壹角'),
('1000000000000', '壹兆元')
];
for I := 0 to High(Tests) do
begin
var Res := DecimalToChineseCurrency(Tests[I].Input);
WriteLn(Format('%s → %s %s', [Tests[I].Input, Res,
IfThen(Res = Tests[I].Expected, '✓', '✗ Exp: ' + Tests[I].Expected)]));
end;
end;
| 階段 | 處理內容 | 關鍵點 |
|---|---|---|
| 輸入驗證 | 僅數字 + 單一小數點 | 拒絕科學記號、貨幣符號、多小數點 | 小數點歸一化 | 補齊 . 位置,移除小數點得純數字串 | DotPos 記錄小數點原始位置,用於計算單位索引 |
| 低位優先處理 | 從個位向高位迴圈 | UnitIdx = 位置 - DotPos,對應 UnitMap 索引 |
| 零值策略 | 四捨五入單位保留單位字 | UnitIdx mod 4 = 1 (元/萬/億/兆) 保留單位 |
| 後處理規則 | 7 條替換規則 | 順序重要:先合併連續零,再處理高階單位前的零 |
中文大寫金額轉換看似簡單,實則包含「零值壓縮」、「單位位權計算」、「四捨五入單位保留」等細節邏輯。本函數以 TStringList 映射表驅動核心轉換,後處理規則鏈修正格式,邏輯清晰、易擴展、可直接用於發票、支票、財務報表列印模組。