思路
我们以万为为单位进行分割成段,思路就是每每个段间的数都几千几百几十的,例如图第一个段内为10,我们就可以说是10-万亿,第二段为1234段内为一千两百三十四,加上段间的单位亿后就是其实际大小,如此做法我们拼接所有段就能实现。
2.难点
(1)段间单位的演算
单位的演变,当数越长时,其段间的单位就是有万与亿的组合且万一定在亿的前面,怎么算呢?段间的单位为段数有关系,我们假设段数为gapCount = 4,段间单位的大小为gapCount/2 = 2,所以是有两个字组成的,gapCount%2 = 0,所以第一个位置为万,其余的单位位置为亿,如果gapCount%2 = 1,则单位上所有的空为亿字,如此可知第四段后的单位为万亿·,符合图片。
(2)有关于各种情况零的处理
此算法中进行了大量有关于零的异常处理,比较多,典型的就是数字中间的0的处理(连续的零和段内千位上的零转化),与零的转化,还有小数零的转化等等情况。
运行结果
给大家看看一个宝贝,我的支票金额(斜眼笑),虽然大数的转化意义不大,但是把马云的钱换为津巴布韦币说不定能用上,开个玩笑。
#include<iostream>
using namespace std;
#define NUM_LENGTH 100//数组大小
//全局变量
int bigNumBeg = 0;//整数的起始下标
int bigNumEnd = 0;//整数的结束下标
bool numFlag = false;//是否存在小数
int smallNumBeg = 0;//小数的起始下标
int wNumBeg = 0;//输出的宽字符数组已使用的位置
bool zeroFlag = false;//判断数字间是否存在连续的零
bool smallInteg = false;//判断小数是否要加整字
bool onlyBig = false;//判断是否只存在整数部分
bool allSmallZero = true;//判断小数部分是否全为零
bool allBigZero = true;//判断整数部分是否全为零
//宽字符数据
struct WNumData
{
wchar_t *wNum;//宽字符指针,指向数组
wchar_t *carry;//段内宽数量级指针,指向数组
wchar_t *gapCarry;//段间宽数量级指针,指向数组
wchar_t *unit;//段间单位指针,指向数组
wchar_t *integer;//指向“整字”的指针
};
/** 数字选择与转化
* num为字符数组的字符元素
* wNum存储转化后的看宽字符数组
* wData宽字符数据
*/
void NumSelect(char num, wchar_t& wNum, WNumData& wData)
{
switch (num)//char型数字字符转化为汉字大写数字
{
case '0':
if (zeroFlag)//段内的第一个零一定可以
{
wNum = wData.wNum[0];
zeroFlag = false;//零的紧接着零不用重复写
}
else//后续的零为空格
{
wNum = ' ';
}
break;
case '1':
wNum = wData.wNum[1];
zeroFlag = true;//后续可为零
break;
case '2':
wNum = wData.wNum[2];
zeroFlag = true;//后续可为零
break;
case '3':
wNum = wData.wNum[3];
zeroFlag = true;//后续可为零
break;
case '4':
wNum = wData.wNum[4];
zeroFlag = true;//后续可为零
break;
case '5':
wNum = wData.wNum[5];
zeroFlag = true;//后续可为零
break;
case '6':
wNum = wData.wNum[6];
zeroFlag = true;//后续可为零
break;
case '7':
wNum = wData.wNum[7];
zeroFlag = true;//后续可为零
break;
case '8':
wNum = wData.wNum[8];
zeroFlag = true;//后续可为零
break;
case '9':
wNum = wData.wNum[9];
zeroFlag = true;//后续可为零
break;
}
}
/** 数量级选择
* len段内数字个数
* wCarry段内数字数量级选择
* wData宽字符数据
*/
void carrySelect(int len, char num, wchar_t& wCarry, WNumData& wData)
{
if (len >= 2 && num != '0')
{
wCarry = wData.carry[len - 2];
}
else
{
wCarry = ' ';
}
}
/** 段间的进制选择
* gapCount为数字以万为单位所分的段数
* wUnit为段段间的数字后面的单位选择
* wData宽字符数据
*/
void unitSelect(int& gapCount, wchar_t* wUnit, WNumData& wData)
{
int gapbigNumBeg = gapCount % 2;//这里的gap不取零从1开始,为了更有序的遍历
int iterCount = gapCount / 2;
if (gapbigNumBeg == 1 && iterCount == 0)//gapCount为1时元为单位,就是万以内的数
{
wUnit[wNumBeg] = wData.unit[0];//赋值圆
wNumBeg++;
}
else//数字大于万时gapCount > 1
{
for (int i = 0; i < iterCount; i++)//进行段间的单位设置,例如“亿亿”,“万亿等”
{
if (gapbigNumBeg == 0)
{
wUnit[wNumBeg] = wData.gapCarry[gapbigNumBeg];
wNumBeg++;
gapbigNumBeg = 1;
}
else
{
wUnit[wNumBeg] = wData.gapCarry[gapbigNumBeg];
wNumBeg++;
}
}
}
}
/** 尾零的排查
* out为输出的宽字符数组
*/
void rearCheckZero(wchar_t *out)
{
int outLength = wNumBeg - 1;
for (int i = outLength; i > 1; i--)
{
if (out[i] != ' ')
{
if (out[i] == (wchar_t)L'零')
{
out[i] = ' ';
}
break;
}
}
}
/** 中零的排查
* begin为查找开始的位置
* len为查找的长度
* bigNum 所被查找的数组
* return 段内中是否全为零
*/
bool midCheckZero(int begin, int len, char *bigNum)
{
bool midZeroFlag = true;
for (int i = 0; i < len; i++)
{
if (bigNum[begin] != '0')
{
midZeroFlag = false;
bigNumBeg = bigNumBeg;
return midZeroFlag;
}
begin++;
}
bigNumBeg = bigNumBeg + 4;
return midZeroFlag;
}
/** 检查小数部分是否为全零。
* beg为查找开始的位置
* precise 小数部分的精度
* smallNum 被查找的数组
*/
void smallZeroCheck(int beg, int precise, char * smallNum)
{
for (int i = 0; i < precise; i++)
{
if (smallNum[beg] != '0')
{
allSmallZero = false;
}
beg++;
}
}
/**检查整数是否全为零
* num指向字符数组的指针
*/
void bigZeroCheck(char *num)
{
for (int i = bigNumBeg; i < bigNumEnd; i++)
{
if (num[i] != '0')
{
allBigZero = false;
return;
}
}
}
/** 输入的字符数组检查
*num 为数字数组指针
* return 字符数组格式是否异常
*/
bool inNumCheck(char *num)
{
int length = strlen(num);
bool numCheckFlag = true;
int dotCheck = 0;
if (num[0] == '.')
{
numCheckFlag = false;
return numCheckFlag;
}
for (int i = 0; i < length; i++)
{
if (num[i] == '.')//查到第一个小数点,如果有第二个或者多个则数据格式有误
{
dotCheck++;
if (dotCheck > 1)
{
numCheckFlag = false;
return numCheckFlag;
}
}
if ((num[i] >= '0'&& num[i] <= '9') || num[i] == '.')
{ }
else
{
numCheckFlag = false;
return numCheckFlag;
}
}
return numCheckFlag;
}
/** 段内的字符数组向宽数组转化
* begin转化开始位置
* bigNum转化数组的指针
* out转化后接受宽字符的数组指针
* gapCount 分段个数
*/
void gapIn(int begin ,int len, char *bigNum, wchar_t *out, WNumData& wData , int& gapCount)
{
//滑动窗口
int i = 0;//窗口起始点
int j = len;//窗口长度
while (i != len)
{
NumSelect(bigNum[begin], out[wNumBeg], wData);
wNumBeg++;//写入一个数字,移位置
carrySelect(j, bigNum[begin], out[wNumBeg], wData);
j--;
wNumBeg++;//段内写入一个单位,移动位置
i++;
begin++;
}
rearCheckZero(out);
unitSelect(gapCount, out, wData);
bigNumBeg = bigNumBeg + i;//记录已经转变的下一个下标
}
/**整数转换
* bigNum转化数组的指针
* out转化后接受宽字符的数组指针
* wData宽字符数据
*/
void TransBig(char *bigNum, wchar_t *out, WNumData& wData)
{
bigZeroCheck(bigNum);//整数零的检查
if (allBigZero)//如果全零则退出函数
{
return;
}
int bigNumLen = bigNumEnd - bigNumBeg;//元素个数
int lenMax = bigNumLen % 4 ;//数字对万取余的后长度,为与最高的数量级位
int gapCount = bigNumLen / 4 + 1;//以万为单位分的段数
bool subFlag = false;//判断getCount是否被lenMax情况减过一次,如果剪过一次则其他情况不减。
while (bigNumBeg != bigNumEnd )//遍历整数部分
{
if (lenMax != 0)//有两种可能,万以内的数字或者剩余的最高位数字
{
gapIn(0, lenMax, bigNum, out, wData, gapCount);
gapCount--;
subFlag = true;
lenMax = 0;//置为零,lenMax只会执行一次
if (gapCount == 0)//万以内的数字,在末尾加整字
{
out[wNumBeg] = wData.integer[0];//添加整字
wNumBeg++;//移动位置
}
continue;
}
else//能万整除的部分,也就是划分的满四个的片段
{
if (!subFlag)
{
gapCount--;
}
for (gapCount; gapCount >=1; gapCount--)
{
if (midCheckZero(bigNumBeg,4,bigNum))//此段内是否全为零,全为零则退出此循环
{
continue;
}
zeroFlag = true;//重置标记,允许各个段‘千位’零的存在
gapIn(bigNumBeg, 4, bigNum, out, wData, gapCount);//当gap=1时选择元为单位
}
out[wNumBeg] = wData.integer[0];//添加整字
wNumBeg++;
}
}
}
/** 小数部分的转换
* smallNum转化数组的指针
* out转化后接受宽字符的数组指针
* wData宽字符数据
*/
void TransSmall(char* smallNum, wchar_t *out, WNumData& wData)
{
int precise = 2;//精度为2,也就是精确到分
int indexTemp = smallNumBeg;//临时存储下标。
//判断是否全为零
smallZeroCheck(smallNumBeg, precise, smallNum);
if (allSmallZero)
{
return;
}
int jIndexTemp = wNumBeg;//临时存储下标
while (!onlyBig)//当不只存在整数部分,删除整数部分多余的整字
{
if (out[jIndexTemp] == L'整')
{
out[jIndexTemp] = ' ';//替换为空格
break;
}
jIndexTemp--;
}
for (int i = 0; i < precise; i++)
{
zeroFlag = true;//单位角上的零可被转化
NumSelect(smallNum[smallNumBeg], out[wNumBeg], wData);
smallNumBeg++;//读取一个字符,移动
wNumBeg++;//写入一个数字,移位置
out[wNumBeg] = wData.unit[i + 1];//写入一个单位,移位置
wNumBeg++;
if (i == 0 && smallNum[indexTemp] != '0' && (smallNum[indexTemp + 1] == '0'
|| smallNum[indexTemp + 1] == '\0'))//角位不为零分位为零,则加整终止。
{
out[wNumBeg] = wData.integer[0];
wNumBeg++;
break;
}
}
}
/** 数字转变
* num转化数组的指针
* out转化后接受宽字符的数组指针
* wData宽字符数据
*/
void TransNum(char *num, wchar_t* out, WNumData& wData )
{
int numLen = 0;//字符数组的长度
while (num[numLen] != '\0')//记录整数部分开始与结束的下标,记录小数部分开始的下标
{
if (num[numLen] == '.' && num[numLen + 1] != '\0')//存在小数点且小数点后不为'\0'
{
numFlag = true;
bigNumEnd = numLen;
numLen++;
smallNumBeg = numLen;
break;
}
numLen++;
}
if (!numFlag)//不存在小数部分
{
onlyBig = true;
bigNumEnd = numLen;
TransBig(num, out, wData);
}
else //存在小数部分,有两种可能,一是只存在小数部分,二是既存在小数部分又存在整数部分
{
if (smallNumBeg == 2 && num[smallNumBeg - 2] == '0')//仅有小数部分
{
onlyBig = true;
TransSmall(num, out, wData);
}
else//即存在不为零的整数部分又存在小数部分
{
onlyBig = false;
TransBig(num, out, wData);
TransSmall(num, out, wData);
}
}
//判断整数部分与小数部分是否全为零如果是则输出零圆整
if (allBigZero && allSmallZero)
{
cout << "零圆整";
return;
}
}
//打印输出
void printOut(wchar_t *out)
{
int i = 0;
wcout.imbue(locale("", LC_CTYPE));
while (out[i] != '\0')
{
if (out[i] != ' ')
{
wcout << out[i];
}
i++;
}
cout << endl;
}
int main()
{
char num[NUM_LENGTH/2];//输入的字符数组
cout << "请输入要转换的数字:";
while (cin >> num)//数字输入
{
if (cin.get() == '\n')
{
break;
}
}
if (!inNumCheck(num))//数字规格检查
{
cout << "数字输入不符合规格,请重新输入" << endl;
system("pause");
return 0;
}
wchar_t out[NUM_LENGTH] = { 0 };//输出的宽字符数组
WNumData wData;
wData.wNum = (wchar_t*)L"零壹贰叄肆伍陆柒捌玖";
wData.carry = (wchar_t*)L"拾佰仟";//段内进制
wData.gapCarry = (wchar_t*)L"万亿";//段间进制
wData.unit = (wchar_t*)L"圆角分";
wData.integer = (wchar_t*)L"整";
TransNum(num, out, wData);//转化
printOut(out);//打印输出
system("pause");
return 0;
}
总结
本人是萌新,如果代码有冗余或者有bug请见谅,可在评论区提出来,我会多多学习的。