前言银行、单位和个人填写的各种票据和结算凭证是办理支付结算和现金收付的重要依据,直接关系到支付结算的准确、及时和安全。
票据和结算凭证是银行、单位和个人凭以记载帐务的会计凭证,是记载经济业务和明确经济责任的一种书面证明。
因此,填写票据和结算凭证,必须做到标准化、规范化,要要素齐全、数字正确、字迹清晰、不错漏、不潦草,防止涂改。
我的服务需要一个金额转换过程,本来想在网上找,但都是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.* @return7.* - NULL 源字符串的格式错误,返回NULL8.* - 非NULL 目的字符串的首地址9.* @note 转换根据:中国人民银行会计司编写的最新《企业、银行正确办理支付结算10.* 指南》的第114页-第115页11.*/12.char* chineseFee( char* dest, char* src )13.{14. enum15. {16.START, //开始17.MINUS, //负号18.ZEROINT, //0整数19.INTEGER, //整数20.DECIMAL, //小数点21.DECIMALfRACTION, //小数位22.END, //结束23.ERROR //错误24. } status = START;25. struct26. {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.else84.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. else97. {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 == DECIMALfRACTION107.|| 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,否则设为1128. if ( feeInfo.sizeDecimal ==0 //没有小数129.|| ( !feeInfo.decimal[ 0 ] && !feeInfo.decimal[ 1 ] ) ) //小数部都为0130.decZeroTag = 1;131. else132.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 ) //处理非段尾0146.{147.zeroTag = 1;148.}149.else if ( feeInfo.integer[ i ] == 0 && !j ) //处理段尾0150.{151.if ( feeInfo.sizeInt == 1 &&decZeroTag ) //特殊处理个位0152. strcat( pDest,NumberChar[ feeInfo.integer[ i ] ] );153.if ( feeInfo.sizeInt != 1 || decZeroTag )154. strcat( pDest, UnitChar[ size - i ] );155.zeroTag = 0;156.}157.else//处理非0158.{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. else175. {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@。