在一次面试中遇到一个有意思的小算法题:
要求将阿拉伯数字转为汉字显示出来(包含单位)。
当时虽然实现出来,但是代码写的有点凌乱。所以回家后,重新整理了一下。
当时虽然实现出来,但是代码写的有点凌乱。所以回家后,重新整理了一下。
这个算法有几个小的注意点:
1、中文字符占两个字节,所以如果用C语言实现,需要用char数组的形式保存单个汉字。
2、如果数字中出现连续的零,需要把它替换为单一的零。
3、在亿、万、元的前面一个汉字不可以为零(人民币读取方式决定)。
4、double数值可分为整数部分和小数部分。小数部分较简单,整数部分需要根据这个数字所在的位数匹配上对应的单位。
具体方法是:设置一个单位映射字符串表g_strUnit,可视为一个简单的HashTable。然后从头开始读取整数字符串的每个
字符,若这个字符在整数字符串的位置为i,这个字符后面的单位就是g_strUnit[length-1-i]。
代码
/******************************************************************************* Project Code : Account File name : Author : Latte Description : 阿拉伯数字转为中文字符串 Function List : -------------------------------------------------------------------------------- History: Date Author Modification Latte created file. *******************************************************************************/ #include "stdafx.h" #include <string> #include <iostream> using namespace std; #define MAX 100 string g_strDigit[] = {"零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"}; string g_strUnit[] = {"圆", "拾", "佰", "仟", "万", "拾", "佰", "仟", "亿", "拾", "佰", "仟", "万", "拾", "佰"}; string g_strUnit2[] = {"角", "分"}; /******************************************************************************* Func Name : ReplaceSubStr Date Created : 2014-07-03 Author : Latte Description : 将源字符串strOrig中第一个匹配strSub的子串部分替换为strReplace Input : string &strOrig, string strSub, string strReplace Output : string &strOrig Return : int Caution : 返回值如果为-1,则表示替换失败或未找到替换项 *******************************************************************************/ int ReplaceSubStr(string &strOrig, string strSub, string strReplace) { int pos = (int)strOrig.find(strSub); int length = (int)strSub.length(); if (pos >= 0) { strOrig.replace(pos, length, strReplace); return 0; } return -1; } /******************************************************************************* Func Name : NumToChineseStr Date Created : 2014-07-03 Author : Latte Description : 将人民币double数值转化为人民币汉字string Input : double money Output : Return : string Caution : *******************************************************************************/ string NumToChineseStr(double money) { int i = 0; int ret = 0; int length = 0; char *p = NULL; char *pcDecimal = NULL; //保存小数部分字符 char czNumber[MAX] = {0}; //保存完整数字部分字符 string strResult; cout << "======================================" << endl; cout << money << endl; //判断是否为小数 if (money < 0) { strResult = "不支持读负数"; return strResult; } //将数字转为数字字符串,利用sprintf_s的正则转换 sprintf_s(czNumber, MAX, "%.15lg", money); printf("[No.0]%s\n", czNumber); //如果数字是太大或太小的数,因为已经转为科学计数,所以会含有e字符 p = strchr(czNumber,'e'); if (NULL!=p) { strResult = "不支持读太大或太小的数"; return strResult; } p = strchr(czNumber, '.'); if (NULL != p) { p[0] = 0; pcDecimal = p + 1; } length = (int)strlen(czNumber); for (i = 0; i<length; i++) { if ('0' == czNumber[i] && 0 != ((length-1-i) % 4)) { strResult += g_strDigit[czNumber[i] - '0']; } else { strResult += g_strDigit[czNumber[i] - '0'] + g_strUnit[length-1-i]; } } cout << "[No.1]把数字直接替换为汉字: \n" << strResult << endl; //把strResult中的所有"零零"子串替换为"零" while (1) { ret = ReplaceSubStr(strResult, "零零", "零"); if (ret < 0) { break; } } cout << "[No.2]替换所有零零为零: \n" << strResult << endl; ReplaceSubStr(strResult, "零亿", "亿"); ReplaceSubStr(strResult, "零万", "万"); if (strResult != "零圆") //如果整数部分全为0,则不要去除元单位前面的零 { ReplaceSubStr(strResult, "零圆", "圆"); } cout << "[No.3]去除零亿、零万、零圆前面的零: \n" << strResult << endl; //小数精确到两位数,即精确到单位分 if (NULL != pcDecimal) { //如果小数部分有数值而整数部分为0,则删除字符串中的零元 if (strResult == "零圆") { strResult.clear(); } i = 0; while (1) { if (0 == pcDecimal[i] || i >= 2) break; strResult += g_strDigit[pcDecimal[i] - '0'] + g_strUnit2[i]; i++; } } cout << "[No.4]小数精确到两位数,即精确到单位分: \n" << strResult << endl; return strResult; } int main(void) { //cout << "Result: " << NumToChineseStr(0.00) << endl; //cout << "Result: " << NumToChineseStr(-345.67) << endl; //cout << "Result: " << NumToChineseStr(1000.0) << endl; cout << "Result: " << NumToChineseStr(130040600090.012) << endl; return 0; }
结果