高分求人民币金额小写转换为大写算法(300分)

  • 主题发起人 主题发起人 gxx
  • 开始时间 开始时间
G

gxx

Unregistered / Unconfirmed
GUEST, unregistred user!
要符合财务规则,我从网上查到一些,都是不符合阅读习惯的那种,什么零佰零拾等。
特向各位高手求教,如赐代码最好,无代码,有完整思路也可。
急盼。
 
已答贴子中很多,建议你查一查,有人回答你也不过是从以前的贴子抄过来而已。
我记得几个贴子有讲得相当详细的,不管是不是符合财务规则,其思路已经相当完整。
甚至有些是成熟的商业代码。你去找找看看吧
 
Function XiaoxieToDaxie(f : String) : String;
var
Fs,dx,d2,zs,xs,h,jg:string;
i,ws,{l,}w,j,lx:integer;
begin
f := Trim(f);
if copy(f,1,1)='-' then begin
Delete(f,1,1);fs:='负';end
else fs:='';
dx:='零壹贰叁肆伍陆柒捌玖';
d2:='拾佰仟万亿';
i := AnsiPos('.',f)
//小数点位置
if i = 0 Then
zs := f //整数
else begin
zs:=copy(f,1,i - 1)
//整数部分
xs:=copy(f,i + 1,200);
end;
ws:= 0
//l := 0;
for i := Length(zs) downto 1 do begin
ws := ws + 1
h := '';
w:=strtoint(copy(zs, i, 1));
if (w=0) and (i=1) then jg:='零';
If w > 0 Then
Case ws of
2..5:h:=copy(d2,(ws-1)*2-1,2);
6..8:begin
h:=copy(d2,(ws-5)*2-1,2);
If AnsiPos('万',jg)=0 Then h:=h+'万';
end;
10..13:h := copy(d2,(ws-9)*2-1, 2);
End;
jg:=copy(dx,(w+1)*2-1,2) + h + jg;
If ws=9 Then jg := copy(jg,1,2) + '亿' + copy(jg,3,200);
end;
j:=AnsiPos('零零',jg);
While j > 0 do begin
jg := copy(jg, 1, j - 1) + copy(jg, j + 2,200);
j := AnsiPos('零零',jg);
end;
If (Length(jg) > 1) And (copy(jg,length(jg)-1,2)='零') Then jg :=copy(jg,1,Length(jg)-2);
j := AnsiPos('零亿',jg);
If j > 0 Then jg := copy(jg,1, j - 1) + copy(jg, j + 2,200);
//转换小数部分
lx := Length(xs);
If lx > 0 Then begin
jg := jg + '元';
For i := 1 To lx do begin
if i=1 then begin
jg := jg + copy(dx, strtoint(copy(xs,i,1))*2 + 1, 2);
jg := jg +'角';
end;
if i=2 then begin
jg := jg + copy(dx, strtoint(copy(xs,i,1))*2 + 1, 2);
jg := jg +'分';
end;
end;
j :=AnsiPos('零角零分',jg);
if j>0 then jg := copy(jg,1,j-1)+copy(jg,j+8,200)+'整';
j := AnsiPos('零角',jg);
if j>0 then jg := copy(jg,1,j-1)+copy(jg,j+4,200);
j := AnsiPos('零分',jg);
if j>0 then jg := copy(jg,1,j-1)+copy(jg,j+4,200);
End
else
jg := jg + '元整';
result := fs+jg;
end;
 
function Tform1.SmallTOBig(small:real):string;
var SmallMonth,BigMonth:string;
wei1,qianwei1:string[2];
wei,qianwei,dianweizhi,qian:integer;
begin
{------- 修改参数令值更精确 -------}
{小数点后的位数,需要的话也可以改动该值}
qianwei:=-2;

{转换成货币形式,需要的话小数点后加多几个零}
Smallmonth:=formatfloat('0.00',small);
{---------------------------------}

dianweizhi :=pos('.',Smallmonth);{小数点的位置}

{循环小写货币的每一位,从小写的右边位置到左边}
for qian:=length(Smallmonth) downto 1 do
begin
{如果读到的不是小数点就继续}
if qian<>dianweizhi then
begin

{位置上的数转换成大写}
case strtoint(copy(Smallmonth,qian,1)) of

1:wei1:='壹'
2:wei1:='贰';
3:wei1:='叁'
4:wei1:='肆';
5:wei1:='伍'
6:wei1:='陆';
7:wei1:='柒'
8:wei1:='捌';
9:wei1:='玖'
0:wei1:='零';
end;

{判断大写位置,可以继续增大到real类型的最大值,可是谁有那么多钱}
case qianwei of
-3:qianwei1:='厘';
-2:qianwei1:='分';
-1:qianwei1:='角';
0 :qianwei1:='元';
1 :qianwei1:='拾';
2 :qianwei1:='佰';
3 :qianwei1:='千';
4 :qianwei1:='万';
5 :qianwei1:='拾';
6 :qianwei1:='佰';
7 :qianwei1:='千';
8 :qianwei1:='亿';
9 :qianwei1:='十';
10:qianwei1:='佰';
11:qianwei1:='千';
end;

inc(qianwei);
BigMonth :=wei1+qianwei1+BigMonth;{组合成大写金额}
end;

end;

SmallTOBig:=BigMonth;

end;
 
什么叫“符合财务规则”?请赐教
 
自己的答案和卷起千堆雪的一样,就不写了
 
to balaschen:
比如:卷起千堆雪tyn大侠的代码:
输入1003.10,
输出为:
壹千零佰零拾叁元壹角零分

我认为就不符合财务规则,起码来说不符合人们日常的阅读习惯。

to commons_sheng:
谢谢你的代码,简单测试了一下,已经相当完美了,但还有一小点缺憾,
如输入1003.02,
输出为:壹仟零叁元贰分,
我觉得输入出为:壹仟零叁元零贰分
才比较理想。
不过我会给你分的。谢谢。
 
我觉得你对财务规则的理解有误,财务数字的表示以单据与报表为基础。与国际财务规范
不同的是中国的大写表达,而大写表达要求足位完整,壹仟零叁元贰分这种表达方式只能
满足阅读习惯,而在标准财务形式来说反而是不正确的。要以票据表达方式为准。
 
昨天刚写了一个Java的,试着用用吧,看看同前几位朋友有什么区别。

/*******************************************************************************
Name: GetBigRMBString<br>
Function: 生成大写中文货币字符串<br>
Author: Leff Lv<br>
Create Date: 2001/9/30<br>
Parameter: double cash<br>
Return value: Big RMB String<br>
******************************************************************************/
public String GetBigRMBString(double dCash){
String strCash = java.text.NumberFormat.getInstance().format(dCash);

while (strCash.indexOf(",") >= 0){
strCash = strCash.substring(0,strCash.indexOf(",")) + strCash.substring(strCash.indexOf(",") + 1,strCash.length());
}
String strRet = "";
boolean bFlag = false;

//得到小数点位置
int nDotPos = strCash.indexOf(".");
if (nDotPos == -1) nDotPos = strCash.length();
//只取小数点后二位
if (nDotPos < strCash.length() - 3){
strCash = strCash.substring(0,strCash.indexOf(".") + 3);
}

//先取小数点后的数
for (int i = nDotPos + 1;i < strCash.length();i++){
if (Integer.parseInt(strCash.substring(i,i+1)) != 0){
strRet = strRet + GetNumber(Integer.parseInt(strCash.substring(i,i+1))) + GetUnit(nDotPos - i);
}
else if(i == nDotPos + 1){
strRet = "零";
}
}

//最后位不为'分'
if (nDotPos == -1 || nDotPos > strCash.length() - 3){
strRet += "整";
}
//取小数点前的数

for (int i = nDotPos - 1;i >= 0;i--){
if (Integer.parseInt(strCash.substring(i,i+1)) != 0){
strRet = GetNumber(Integer.parseInt(strCash.substring(i,i+1))) + GetUnit(nDotPos - i) + strRet;
bFlag = false;
}
else {
if (! bFlag &amp;&amp
nDotPos - i != 1) strRet = "零" + strRet;
bFlag = true;

if (nDotPos - i == 5 || nDotPos - i == 9 || nDotPos - i == 13){
strRet = GetUnit(nDotPos - i) + strRet;
}
else if (nDotPos - i == 1){
strRet = "元" + strRet;
}
}
}
return strRet;
}

public String GetNumber(int nNum){
String strRet = "";
switch (nNum){
case 0 : strRet = "零"
break;
case 1 : strRet = "壹"
break;
case 2 : strRet = "贰"
break;
case 3 : strRet = "叁"
break;
case 4 : strRet = "肆"
break;
case 5 : strRet = "伍"
break;
case 6 : strRet = "陆"
break;
case 7 : strRet = "柒"
break;
case 8 : strRet = "捌"
break;
case 9 : strRet = "玖"
break;
}
return strRet;
}

public String GetUnit(int nUnit){
String strRet = "";
switch(nUnit){
case -2 : strRet = "分";break;
case -1 : strRet = "角";break;
case 1 : strRet = "圆";break;
case 2 : strRet = "拾";break;
case 3 : strRet = "佰";break;
case 4 : strRet = "仟";break;
case 5 : strRet = "万";break;
case 6 : strRet = "拾";break;
case 7 : strRet = "佰";break;
case 8 : strRet = "仟";break;
case 9 : strRet = "亿";break;
case 10 : strRet = "拾";break;
case 11 : strRet = "佰";break;
case 12 : strRet = "仟";break;
case 13 : strRet = "万";break;
}
return strRet;
}
 
千堆雪给的代码肯定是有问题的,不能这样一个数字一个数字的转换
比如 100000 得到的结果就会是
“壹拾零万零千零百零拾零角零分”
而正确答案应该是 “壹拾万”

下面是我回复另一个帖子的答案:

-------------------------------------------------------------------------
手上没有Delphi,中午用VC写了一段,但愿对你有帮助。

这段程序跟楼上的有点像,都是基于查表格的
没有实现小数部分,但是加入对小数的支持很容易,如果不是特别急的
话留给你练练手了

程序基本思路是将数字字串按四位分割,每隔四位从t_unit1表中选择单位
插到数字后,在四位内则从t_unit2表中选择单位,总体流程并不复杂,
需要注意的是对数字0的处理,比如5001是不能转换成五百零零一的,未解决
这个问题,我用了一个标志old_inx_num来判断前面是否已经有了零了。


程序执行

输入 m_szCurrency = 500156736020056
输出 s = 伍佰万壹千伍佰陆拾柒亿叁千陆佰零贰万零伍拾陆园


CString t_unit1[5] = {"园", "万", "亿", "万", "亿"};
CString t_unit2[3] = {"拾", "佰", "千"};
CString t_num[10] = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

CString s = "";
int old_inx_num = 0;
for ( int i = 0
i < m_szCurrency.GetLength()
i++ )
{
char x = 0;
int inx_num = m_szCurrency[ m_szCurrency.GetLength() - i - 1 ] - '0';
int inx_unit;

if ( i % 4 == 0 )
{
inx_unit = i / 4;
if (inx_num)
{
s = t_num[ inx_num ] + t_unit1[ inx_unit ] + s;
}
else
{
s = t_unit1[ inx_unit ] + s;
}
}
else
{
inx_unit = i % 4 - 1;
if (inx_num)
{
s = t_num[ inx_num ] + t_unit2[ inx_unit ] + s;
}
else
{
if (old_inx_num)
s = t_num[ inx_num ] + s;
}
}
old_inx_num = inx_num;
}


 
哈哈,我的没有这种问题。
 
还是对规则的理解不同,我的用户还特意要我显示成“壹拾零万零千零百零拾零角零分”
这种形式
 
这个应该符合你的要求了
Function XiaoxieToDaxie(f : String) : String;
var
Fs,dx,d2,zs,xs,h,jg:string;
i,ws,{l,}w,j,lx:integer;
begin
f := Trim(f);
if copy(f,1,1)='-' then begin
Delete(f,1,1);fs:='负';end
else fs:='';
dx:='零壹贰叁肆伍陆柒捌玖';
d2:='拾佰仟万亿';
i := AnsiPos('.',f)
//小数点位置
if i = 0 Then
zs := f //整数
else begin
zs:=copy(f,1,i - 1)
//整数部分
xs:=copy(f,i + 1,200);
end;
ws:= 0
//l := 0;
for i := Length(zs) downto 1 do begin
ws := ws + 1
h := '';
w:=strtoint(copy(zs, i, 1));
if (w=0) and (i=1) then jg:='零';
If w > 0 Then
Case ws of
2..5:h:=copy(d2,(ws-1)*2-1,2);
6..8:begin
h:=copy(d2,(ws-5)*2-1,2);
If AnsiPos('万',jg)=0 Then h:=h+'万';
end;
10..13:h := copy(d2,(ws-9)*2-1, 2);
End;
jg:=copy(dx,(w+1)*2-1,2) + h + jg;
If ws=9 Then jg := copy(jg,1,2) + '亿' + copy(jg,3,200);
end;
j:=AnsiPos('零零',jg);
While j > 0 do begin
jg := copy(jg, 1, j - 1) + copy(jg, j + 2,200);
j := AnsiPos('零零',jg);
end;
If (Length(jg) > 1) And (copy(jg,length(jg)-1,2)='零') Then jg :=copy(jg,1,Length(jg)-2);
j := AnsiPos('零亿',jg);
If j > 0 Then jg := copy(jg,1, j - 1) + copy(jg, j + 2,200);
//转换小数部分
lx := Length(xs);
If lx > 0 Then begin
jg := jg + '元';
For i := 1 To lx do begin
if i=1 then begin
jg := jg + copy(dx, strtoint(copy(xs,i,1))*2 + 1, 2);
jg := jg +'角';
end;
if i=2 then begin
jg := jg + copy(dx, strtoint(copy(xs,i,1))*2 + 1, 2);
jg := jg +'分';
end;
end;
j :=AnsiPos('零角零分',jg);
if j>0 then jg := copy(jg,1,j-1)+copy(jg,j+8,200)+'整';
j := AnsiPos('零角',jg);
i:=AnsiPos('零分',jg);
if (j>0) and (i>0) then jg := copy(jg,1,j-1)+copy(jg,j+4,200)
else jg:= copy(jg,1,j+1)+copy(jg,j+4,200);
 
没有写完,后面接上
End
else
jg := jg + '元整';
result := fs+jg;
end;
 
To balaschen:
我想像把 100230000.00 写成  壹亿零千零佰贰拾叁万零千零佰零拾零元零角零分
这样的形式应该是不合适的,但是如果转换成
壹亿零千万零佰万零贰拾叁万零千零佰零拾零元零角零分 倒是可以的

 
按oceanwave的建议,我又找了一下以前的贴子,果然是个古老的话题了,
不过,每次讨论,好象都有新的答案出现,也是一种进步吧!

问题已基本解决。

我想如此分配分数,听听大家的意见:
oceanwave:30分
commons_sheng:100分
吕雪松:50分
cheka:50分
留下点分等继续讨论,不知各位认为怎样?

 
关于财务规范,找了点资料。

正确填写票据和结算凭证的基本规定

银行、单位和个人填写的各种票据和结算凭证是办理支付结算和现金收付的重要依据,
直接关系到支付结算的准确、及时和安全。票据和结算凭证是银行、单位和个人凭以记载账
务的会计凭证,是记载经济业务和明确经济责任的一种书面证明。因此,填写票据和结算凭
证,必须做到标准化、规范化,要要素齐全、数字正确、字迹清晰、不错漏、不潦草,防止
涂改。
一、中文大写金额数字应用正楷或行书填写,如壹(壹)、贰(贰)、叁、肆(肆)、
伍(伍)、陆(陆)、柒、捌、玖、拾、佰、仟、万(万)、亿、元、角、分、零、整(
正)等字样。不得用一、二(两)、三、四、五、六、七、八、九、十、念、毛、另(或0)
填写,不得自造简化字。如果金额数字书写中使用繁体字,如貳、陸、億、萬、圓的,也
应受理。
二、中文大写金额数字到“元”为止的,在“元”之后,应写“整”(或“正”)字,
在“角”之后可以不写“整”(或“正”)字。大写金额数字有“分”的,“分”后面不
写“整”(或“正”)字。
三、中文大写金额数字前应标明“人民币”字样,大写金额数字应紧接“人民币”字
样填写,不得留有空白。大写金额数字前未印“人民币”字样的,应加填“人民币”三字。
在票据和结算凭证大写金额栏内不得预印固定的“仟、佰、拾、万、仟、伯、拾、元、角、
分”字样。
四、阿拉伯小写金额数字中有“0”时,中文大写应按照汉语语言规律、金额数字构
成和防止涂改的要求进行书写。举例如下:
(一)阿拉伯数字中间有“O”时,中文大写金额要写“零”字。如¥1,409.50,
应写成人民币壹仟肆佰零玖元伍角。
(二)阿拉伯数字中间连续有几个“0”时,中文大写金额中间可以只写一个“零”
字。如¥6,007.14,应写成人民币陆仟零柒元壹角肆分。
(三)阿拉伯金额数字万位或元位是“0”,或者数字中间连续有几个“0”,万位、
元位也是“0’,但千位、角位不是“0”时,中文大写金额中可以只写一个零字,也可
以不写“零”字。如¥1,680.32,应写成人民币壹仟陆佰捌拾元零叁角贰分,或者写
成人民币壹仟陆佰捌拾元叁角贰分;又如¥107,000.53,应写成人民币壹拾万柒仟元
零伍角叁分,或者写成人民币壹拾万零柒仟元伍角叁分。
(四)阿拉伯金额数字角位是“0”,而分位不是“0”时,中文大写金额“元”后
面应写“零”字。如¥16,409.02,应写成人民币壹万陆仟肆佰零玖元零贰分;又如
¥325.04,应写成人民币叁佰贰拾伍元零肆分。
五、阿拉伯小写金额数字前面,均应填写入民币符号“¥”(或草写:)。阿拉伯
小写金额数字要认真填写,不得连写分辨不清。
六、票据的出票日期必须使用中文大写。为防止变造票据的出禀日期,在填写月、
日时,月为壹、贰和壹拾的,日为壹至玖和壹拾、贰拾和叁抬的,应在其前加“零”;
日为抬壹至拾玖的,应在其前加“壹”。如1月15日,应写成零壹月壹拾伍日。再如
10月20日,应写成零壹拾月零贰拾日。
七、票据出票日期使用小写填写的,银行不予受理。大写日期未按要求规范填写的,
银行可予受理,但由此造成损失的,由出票入自行承担。

 
看来,我来晚了。
我看了一下,大部分答案都是网上摘下来的,实际用起来都有缺陷。
甚至有人还讨论起来财务规划和阅读习惯。(其实,这方面财务制度上有严格规定的,
差一点都不行的,我是银行职员,当然非常清楚这点)
我以前做过这个函数,先看了网上的,都不对,后来还是自己写了一个。虽然代码乱了点,
但功能能够全部达到要求,只是,你们的问题都解决了,我就不用把那代码从箱子底下
翻出来了。
 
黄凯有个这方面的控件,有原码,挺不错的。
 
给刚才的程序加了对小数的支持,不敢说符合财务规则(我不懂),
但应该符合阅读习惯

12303004.03
转换为
壹千贰佰叁拾万叁千零肆园零角叁分

CString SmallToBig(const CString&amp
szCurrency)
{
int dotPos = szCurrency.Find(".",0);// 从左起小数点的位置
if ( -1 == dotPos ) // 没有找到小数点
dotPos = szCurrency.GetLength();
CString szInt;
CString szFlt;
szInt = szCurrency.Left(dotPos);
szFlt = szCurrency.Right( szCurrency.GetLength() - dotPos -1 );
if ( (szFlt.GetLength()!=2) &amp;&amp
(szFlt.GetLength()!=0)) return "";
// 小数点后必须有两位或者根本没有

CString t_unit1[5] = {"园", "万", "亿", "万", "亿"};
CString t_unit2[3] = {"拾", "佰", "千"};
CString t_unit3[2] = {"角", "分"};
CString t_num[10] = { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"};

CString s = "";

// 处理整数部分,不是逐字替换
int old_inx_num = 0;
for ( int i = 0
i < szInt.GetLength()
i++ )
{
char x = 0;
int inx_num = szInt[ szInt.GetLength() - i -1 ] - '0';// 将字符映射到数字
int inx_unit;

if ( i % 4 == 0 )
{
inx_unit = i / 4;
if (inx_num)
s = t_num[ inx_num ] + t_unit1[ inx_unit ] + s

else
s = t_unit1[ inx_unit ] + s;
}
else
{
inx_unit = i % 4 - 1;
if (inx_num)
s = t_num[ inx_num ] + t_unit2[ inx_unit ] + s

else
if (old_inx_num)
s = t_num[ inx_num ] + s;
}
old_inx_num = inx_num;
}
// 处理小数部分
if ( szFlt!="" )
{
for (int i = 0
i < 2
i++)
{
int inx_num = szFlt - '0';
if (inx_num)
s = s + t_num[inx_num] + t_unit3;
else
s = s + t_num[inx_num];
}
}
else
{
s = s + "整";
}
return "人民币" + s;
}
 

Similar threads

S
回复
0
查看
3K
SUNSTONE的Delphi笔记
S
S
回复
0
查看
2K
SUNSTONE的Delphi笔记
S
I
回复
0
查看
724
import
I
后退
顶部