免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 11394 | 回复: 6
打印 上一主题 下一主题

小写金额转换为大写金额(C实现) [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2005-08-02 17:40 |只看该作者 |倒序浏览
小写金额转换为大写金额(C实现)
前言
  银行、单位和个人填写的各种票据和结算凭证是办理支付结算和现金收付的重要依据,直接关系到支付结算的准确、及时和安全。票据和结算凭证是银行、单位和个人凭以记载帐务的会计凭证,是记载经济业务和明确经济责任的一种书面证明。因此,填写票据和结算凭证,必须做到标准化、规范化,要要素齐全、数字正确、字迹清晰、不错漏、不潦草,防止涂改。

  我的服务需要一个金额转换过程,本来想在网上找,但都是C++、JavaScript、Delphi的Demo,还没有C的。索性自己写一个。参考了其它的转换算法,对我有些启发。
  大多的算法都是直接分析字符串生成大写金额,即存在一个假设:源字符串的格式是正确的。在我的过程中,用状态机的方法分析源字符串,错误时,返回空指针(我可不敢保证传给我的过程的都是##.##)。 分析出源字符串中整数部有多少个数字,是否有小数,统计结果放在一个结构体中,整数和小数部分的数字分别放在两个整形数组里。
  有了统计数据就可以生成大写金额了。转换过程有个难点:要区分万、亿等“段”,特别是个位这个“段”,这个概念是在《小写转大写金额在C++中的实现》文章中提到的。在下面的程序中用j= ( size - i - 1 ) & 0x3,实际上是j = ( size - i - 1 ) % 4取模,j==0时为段尾,需要特殊处理。所有的处理都是围绕0来进行的,也就是说,0才是难点。
  特殊位置的0,按段分,段中第一个非0数字前的0,可能有多个;段中两个非0数字间的0;段尾的0;个位的0;十分位,角位置的0。
  另外,转换的一个重点是大写金额的写法,好像大多的算法都注重转换过程而对这个问题没有深究。我在文章后面附上转换规则。
下面是代码
  1. /**
  2. * @brief 将源字符串中的小写金额转换为大写格式
  3. *
  4. * @param dest 目的字符串
  5. * @param src  小写金额字符串
  6. * @return
  7. * - NULL 源字符串的格式错误,返回NULL
  8. * - 非NULL 目的字符串的首地址
  9. * @note 转换根据:中国人民银行会计司编写的最新《企业、银行正确办理支付结算
  10. *       指南》的第114页-第115页
  11. */
  12. char* chineseFee( char* dest, char* src )
  13. {
  14.     enum
  15.     {
  16.         START,                 //开始
  17.         MINUS,                 //负号
  18.         ZEROINT,               //0整数
  19.         INTEGER,               //整数
  20.         DECIMAL,               //小数点
  21.         DECIMALfRACTION,       //小数位
  22.         END,                   //结束
  23.         ERROR                  //错误
  24.     } status = START;
  25.     struct
  26.     {
  27.         int minus;             //0为正,1为负
  28.         int sizeInt;
  29.         int sizeDecimal;
  30.         int integer[10];
  31.         int decimal[10];
  32.     } feeInfo;
  33.     char* NumberChar[] =
  34.         { "零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖" };
  35.     char* UnitChar[] =
  36.         { "整", "圆", "拾", "佰","仟", "万", "拾", "佰", "仟", "亿",
  37.           "拾", "佰", "仟", "万亿", "拾", "佰", "仟", "亿亿",
  38.           "角", "分", "负", "人民币" };
  39.          
  40.     int      i, j,size;             //循环变量
  41.     int      zeroTag        = 0,    //0标志
  42.              decZeroTag     = 0;
  43.       
  44.     char*    pDest          = dest;
  45.     char*    pSrc           = src;
  46.    
  47.     int*     pInt           = feeInfo.integer;
  48.     int*     pDec           = feeInfo.decimal;
  49.    
  50.     //初始化
  51.     feeInfo.sizeInt        = 0;
  52.     feeInfo.sizeDecimal    = 0;
  53.     feeInfo.minus          = 0;
  54.    
  55.     //分析字符串
  56.     while( 1 )
  57.     {
  58.         switch ( *pSrc )
  59.         {
  60.             case '-' :
  61.                 status = ( status == START ) ? MINUS : ERROR;
  62.                 feeInfo.minus = ( status == MINUS ) ? 1 : 0;
  63.                 break;
  64.             case '1' :
  65.             case '2' :
  66.             case '3' :
  67.             case '4' :
  68.             case '5' :
  69.             case '6' :
  70.             case '7' :
  71.             case '8' :
  72.             case '9' :
  73.             case '0' :
  74.                 if ( *pSrc == '0' && status == ZEROINT )//|| status == START ) )
  75.                 {
  76.                     status = ERROR;
  77.                     break;
  78.                 }
  79.                 if ( status == MINUS || status == START || status == INTEGER )
  80.                 {
  81.                     if ( *pSrc == '0' && ( status == MINUS || status == START ) )
  82.                         status = ZEROINT;
  83.                     else
  84.                         status = INTEGER;
  85.                     *pInt = (*pSrc) - 48;
  86.                     ++pInt;
  87.                     ++feeInfo.sizeInt;
  88.                 }
  89.                 else if ( status == DECIMAL || status == DECIMALfRACTION )
  90.                 {
  91.                     status = DECIMALfRACTION;
  92.                     *pDec = (*pSrc) - 48;
  93.                     ++pDec;
  94.                     ++feeInfo.sizeDecimal;
  95.                 }
  96.                 else
  97.                 {
  98.                     status =ERROR;
  99.                 }
  100.                 break;
  101.             case '.' :
  102.                 status = ( status == INTEGER || status == ZEROINT )
  103.                          ? DECIMAL : ERROR;
  104.                 break;
  105.             case '\0' :
  106.                 status = ( status == INTEGER || status == DECIMALfRACTION
  107.                            || status == ZEROINT ) ? END : ERROR;
  108.                 break;
  109.             default :
  110.                 status = ERROR;
  111.         }
  112.         if ( status == END )
  113.             break;
  114.         else if ( status == ERROR )
  115.             return NULL;
  116.         
  117.         ++pSrc;
  118.     }
  119.    
  120.     //只有1位小数时,设置百分位为0,使下面代码不需要区分这两种情况
  121.     if ( feeInfo.sizeDecimal == 1 )
  122.     {
  123.         feeInfo.decimal[ 1 ] = 0;
  124.         ++feeInfo.sizeDecimal;
  125.     }
  126.     //判断是否需要打印小数部分,有小数部且十分位和百分位不都为0
  127.     //需要打印小数部时,zeroTag设为0,否则设为1
  128.     if ( feeInfo.sizeDecimal == 0                                 //没有小数
  129.         || ( !feeInfo.decimal[ 0 ] && !feeInfo.decimal[ 1 ] ) )   //小数部都为0
  130.         decZeroTag = 1;
  131.     else
  132.         decZeroTag = 0;
  133.         
  134.     //printf( "int size: %d    decimal size: %d\n", feeInfo.sizeInt, feeInfo.sizeDecimal );
  135.    
  136.     strcpy( pDest, UnitChar[ 21 ] );                  //初始化目标字符串-人民币
  137.    
  138.     if ( feeInfo.minus ) strcat( pDest, UnitChar[ 20 ] );    //负号
  139.         
  140.     //处理整数部分
  141.     size = feeInfo.sizeInt;
  142.     for( i = 0; i < size; ++i )
  143.     {
  144.         j = size - i - 1 & 0x3;                              //j = 0时为段尾
  145.         if ( feeInfo.integer[ i ] == 0 && j )                //处理非段尾0
  146.         {
  147.             zeroTag = 1;
  148.         }
  149.         else if ( feeInfo.integer[ i ] == 0 && !j )          //处理段尾0
  150.         {
  151.             if ( feeInfo.sizeInt == 1 && decZeroTag )        //特殊处理个位0
  152.                 strcat( pDest, NumberChar[ feeInfo.integer[ i ] ] );
  153.             if ( feeInfo.sizeInt != 1 || decZeroTag )
  154.                 strcat( pDest, UnitChar[ size - i ] );
  155.             zeroTag = 0;
  156.         }
  157.         else                                                 //处理非0
  158.         {
  159.             if ( zeroTag )
  160.             {
  161.                 strcat( pDest, NumberChar[ 0 ] );
  162.                 zeroTag = 0;
  163.             }
  164.             strcat( pDest, NumberChar[ feeInfo.integer[ i ] ] );
  165.             strcat( pDest, UnitChar[ size - i ] );
  166.             if ( !j ) zeroTag = 0;                      //如果是段尾,设为非标志
  167.         }
  168.     }
  169.    
  170.     if ( decZeroTag )
  171.     {
  172.         strcat( pDest, UnitChar[ 0 ] );//没有小数部,打印"整"字符
  173.     }
  174.     else
  175.     {
  176.         //十分位
  177.         if ( feeInfo.decimal[ 0 ] )
  178.         {
  179.             strcat( pDest, NumberChar[ feeInfo.decimal[ 0 ] ] );
  180.             strcat( pDest, UnitChar[ 18 ] );
  181.         }
  182.         else if ( feeInfo.sizeInt != 1 || feeInfo.integer[ 0 ] )
  183.         {
  184.             strcat( pDest, NumberChar[ feeInfo.decimal[ 0 ] ] );
  185.         }
  186.         
  187.         //百分位不为0时
  188.         if ( feeInfo.decimal[ 1 ] )   
  189.         {
  190.             strcat( pDest, NumberChar[ feeInfo.decimal[ 1 ] ] );
  191.             strcat( pDest, UnitChar[ 19 ] );
  192.         }
  193.     }
  194.     return dest;
  195. }
复制代码

  代码中有些地方没有注释清楚,要是有不明白的可以E-Mail我z_jingwei@163.com。

参考:
小写转大写金额在C++中的实现

附:
正确填写票据和结算凭证的基本规定
银行、单位和个人填写的各种票据和结算凭证是办理支付结算和现金收付的重要依据,直接关系到支付结算的准确、及时和安全。票据和结算凭证是银行、单位和个人凭以记载帐务的会计凭证,是记载经济业务和明确经济责任的一种书面证明。因此,填写票据和结算凭证,必须做到标准化、规范化,要要素齐全、数字正确、字迹清晰、不错漏、不潦草,防止涂改。
一、 中文大写金额数字应用正楷或行书填写,如壹(壹)、贰(贰)、叁、肆(肆)、伍(伍)、陆(陆)、柒、捌、玖、拾、伯、仟、万(万)、亿、元、角、分、零、整(正)等字样。不得用一、二(两)、三、四、五、六、七、八、九、十、念、毛、另(或0)填写,不得自造简化字。如果金额数字书写中使用繁体字,也应受理。
二、 中文大写金额数字到"元"为止的,在"元"之后,应写"整"(或"正"字,在"角"之后可以不写"整"(或"正"字。数字有"分"的,"分"后面不写"整"(或"正"字。
三、 中文大写金额数字前应标明"人民币"字样,大写金额数字应紧接"人民币"字样填写,不得留有空白。大写金额数字前未印"人民币"字样的,应加填"人民币"三字。在票据和结算凭证大写金额栏内不得预印固定的"仟、佰、拾、万、仟、佰、拾、元、角、?quot;字样。
四、阿拉伯小写金额数字中有"0"时,中文大写应按照汉语语言规律、金额数字构成和防止涂改的要求进行书写。举例如下:
(一)阿拉伯数字中间有"0"时,中文大写金额要写"零"字。如¥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日,应写成零壹拾月零贰拾日。
七、票据出票日期使用小写填写的,银行不予受理。大写日期未按要求规范填写的,银行可予受理,但由此造成损失的,由出票人自行承担。
引自中国人民银行会计司编写的最新《企业、银行正确办理支付结算指南》的第114页-第115页

论坛徽章:
0
2 [报告]
发表于 2005-08-02 18:05 |只看该作者

小写金额转换为大写金额(C实现)

呵呵,好东西,不过可惜我不搞这些

BTW: 楼主你确定上面的东西没版权的么?公司的东西的话最好不要拿出来

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
3 [报告]
发表于 2005-08-02 18:36 |只看该作者

小写金额转换为大写金额(C实现)

记得几个月前 大家还写这个了.
当时没有测试用例.

论坛徽章:
1
荣誉版主
日期:2011-11-23 16:44:17
4 [报告]
发表于 2005-08-03 08:57 |只看该作者

小写金额转换为大写金额(C实现)

学校的练习而已,楼主做的认真了。赞一个。

论坛徽章:
0
5 [报告]
发表于 2005-08-03 09:38 |只看该作者

小写金额转换为大写金额(C实现)

如果是练习,咱也赞一个

论坛徽章:
0
6 [报告]
发表于 2005-08-03 10:42 |只看该作者

小写金额转换为大写金额(C实现)

晕~~我的服务要用,练习也不用这个了

论坛徽章:
0
7 [报告]
发表于 2014-11-21 22:06 |只看该作者
太赞了,感谢楼主的代码,帮了大忙了
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP