在一次面试中遇到一个有意思的小算法题:要求将阿拉伯数字转为汉字显示出来(包含单位)。
当时虽然实现出来,但是代码写的有点凌乱。所以回家后,重新整理了一下。
这个算法有几个小的注意点:
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
20140703 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 ", 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]把数字直接替换为汉字: " << strResult << endl;
//把strResult中的所有"零零"子串替换为"零"
while (1)
{
ret = ReplaceSubStr(strResult, "零零", "零");
if (ret < 0)
{
break;
}
}
cout << "[No.2]替换所有零零为零: " << strResult << endl;
ReplaceSubStr(strResult, "零亿", "亿");
ReplaceSubStr(strResult, "零万", "万");
if (strResult != "零圆") //如果整数部分全为0,则不要去除元单位前面的零
{
ReplaceSubStr(strResult, "零圆", "圆");
}
cout << "[No.3]去除零亿、零万、零圆前面的零: " << 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]小数精确到两位数,即精确到单位分: " << 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;
}
Project Code : Account
File name :
Author : Latte
Description : 阿拉伯数字转为中文字符串
Function List :
--------------------------------------------------------------------------------
History:
Date Author Modification
20140703 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 ", 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]把数字直接替换为汉字: " << strResult << endl;
//把strResult中的所有"零零"子串替换为"零"
while (1)
{
ret = ReplaceSubStr(strResult, "零零", "零");
if (ret < 0)
{
break;
}
}
cout << "[No.2]替换所有零零为零: " << strResult << endl;
ReplaceSubStr(strResult, "零亿", "亿");
ReplaceSubStr(strResult, "零万", "万");
if (strResult != "零圆") //如果整数部分全为0,则不要去除元单位前面的零
{
ReplaceSubStr(strResult, "零圆", "圆");
}
cout << "[No.3]去除零亿、零万、零圆前面的零: " << 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]小数精确到两位数,即精确到单位分: " << 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;
}
结果