易码技术论坛

 找回密码
 加入易码
搜索
查看: 1068|回复: 7

[源码] 用C写个中文数字转换函数

[复制链接]
发表于 2009-11-22 01:17:05 | 显示全部楼层 |阅读模式
如题,函数形式为:
long Num(char str[]){...}
要求能实现如下转换:
"千">>1000
"五一二">>512
"二百三十">>230
"九十万">>900000
"十亿二百万零廿三">>1002000023
"万万">>100000000

大家来出出主意.
5L代码文曲星用,函数形式为long numch(int pt);
7L代码C用,函数形式为double numch(char *str);

[ 本帖最后由 LuoJun_LZ 于 2009-12-24 17:04 编辑 ]
发表于 2009-11-22 21:40:48 | 显示全部楼层
"五一二">>512
"二百三十">>230

仅两个就无法区分啊。你这个要求给得太模糊了。
 楼主| 发表于 2009-11-23 00:02:04 | 显示全部楼层
这两个可以区分呀.
"五一二"中没有表示位数的文字,可用循环实现以下式子:
512=((5*10)+1)*10+2;
而"二百三十"中有两个表示位数的文字~~计算方式变为:
230=2*100+3*10
两种算法综合后,可把:
如"三五百一十"转为:3510
发表于 2009-11-23 12:59:54 | 显示全部楼层
汗……原来是中文转阿拉伯数字啊。
我还以为是阿拉伯数字转中文=_=
 楼主| 发表于 2009-11-23 17:49:12 | 显示全部楼层
汗,从函数形式就能看出来了呀~ 不过也可以弄个数字转中文的
 楼主| 发表于 2009-12-11 21:48:12 | 显示全部楼层
汗,都没什么人回应~~

我在文曲星上用LAV1写了一个,分享下:
char _NUMBER[]="零一壹二贰三叁四肆五伍六陆七柒八捌九玖十拾廿百佰千仟万兆亿";

long _Num(int pt1,int pt2,char len)//各位数字,位,位数
{//递归辅助函数
long tmp, i, a, btmp;
int w;//位数指针
char c;//指针
char k;
char t[64];
char tt[16];

w = len - 1;
while(w>=0)//逆序检测顺序是否由大到小
{
  if(*(pt2+w-1)<=*(pt2+w))
  {
   c = w;
   while(*(pt2+w-1)<=*(pt2+c) && w)
    w--;
   len = len - c + w;

   memcpy(&tmp,pt1+c*4,4);//取升位数
   memcpy(t,pt1+4+c*4,60-c*4);//移动
   memcpy(tt,pt2+c,15-c);//移动
   tmp = _Num(pt1+w*4,pt2+w,c-w)+tmp;//递归赋值
   memcpy(pt1+w*4,&tmp,4);
   memcpy(pt1+4+w*4,t,60-c*4);//移动
   memcpy(pt2+w,tt,15-c);//移动
  }
  w--;
}

a = 0;
c = 0;
while(c<len)//计算
{
  memcpy(&tmp,pt1+c*4,4);
  k = *(pt2+c);
  if(10 == k)
   i = 10;
  else
   if(11 == k)
    i = 20;
  else
   if(12 == k)
    i = 100;
  else
   if(13 == k)
    i = 1000;
  else
   if(14 == k)
    i = 10000;
  else
   if(15 == k)
    i = 1000000;
  else
   if(16 == k)
    i = 100000000;
  else
   i = 1;
  
  if(tmp)
   a = a + i*tmp;
  else
   if(k)
    a = a + i;
  c++;
}
return a;
}
long numch(int pt)
{//转成非负整数
char x,k;
char w[16], len;
int b;
long num[16];

memset(num,0,64);
memset(w,0,16);

b = pt;
len = 0;
k = 1;

while(len<16 && *b)//循环1,取得数字代号
{
  if(*b<128)
  {
   x = *b-'0';
   if(x > 9)
    break;
   b--;
  }
  else
  {
   memcpy(&nc,b,2);
   x = 0;
   while(x<29 && (*b != _NUMBER[x*2] || *(b+1) != _NUMBER[x*2+1]))x++;//取得实际数字
   if(x==29)
    break;//遇到非数字字符
   if(x<21)
    x = x+1>>1;
   else if(x==21)
    x = 11;
   else if(x<26)
    x = x + 2 >>1;
   else x = x - 12;
  }
  if(x>=10)
  {
   *(w+len)=x;
   len++;
   k = 1;
  }
  else
  {
   if(!x)
    k = 0;
   num[len] = 10*num[len]+x;//记录
  }
  b = 2 + b;
  while(*b==' ')b++;//消除空格
  if(*b ==',')b++;//遇到一个逗号
}
if(b == pt)
  return -1;//出错
if(!len)//普通类型转换
  return num[0];
if(num[len])
{
  if(k)
   if(!w[len])//处理如“二百五”=250的情况
    if(w[len-1]>12)
     w[len] = w[len-1]-1;
    else
     if(w[len-1]==12)
      w[len] = 10;
  len ++;
}
return _Num(num,w,len);
}

//支持类型如:(实现中文大小写和阿拉伯数字混合使用)
//"1,000,000"=1000000;
//"1 2 3 4  5"=12345
//"一三五"=135
//"1百3十"=130;
//"五拾叁万"=530000
//"贰佰5"=250
//"贰佰05"=205
//"百万"=1000000

[ 本帖最后由 LuoJun_LZ 于 2009-12-22 16:24 编辑 ]
发表于 2009-12-13 20:56:21 | 显示全部楼层
好样的。好久没人在WQX上写什么程序了
 楼主| 发表于 2009-12-16 21:48:30 | 显示全部楼层
其实这个函数速度较慢,
文曲星上转换“一十九亿九千九百九十九万九千九百九十九”=1999999999
要0.26秒

又用C写了个,几乎完全注释:

#define MAX 16 //最大位数

char _NUMBER[59]="零一壹二贰三叁四肆五伍六陆七柒八捌九玖十拾廿百佰千仟万兆亿";

double _Num(double *num,char *wei,char len)//各位对应数字,位,位数
{//递归辅助函数
      double i, a, tmp;//临时数字
      int w;//位数指针
      char c;//指针

      w = len - 1;

      while(w>0)//逆序检测单位顺序是否由大到小,如“五千万五千”的单位顺序就不是由大到小
      {
            if(*(wei+w-1) <= *(wei+w))//前一个单位小于或等于后一个单位
            {
                  c = w;//记录后一单位
                  w--;

                  while(*(wei+w-1)<=*(wei+c) && w)//检索到前一个单位大于记录单位为止
                        w--;

                  *(num+w) = _Num(num+w,wei+w,c-w) + *(num+c);//递归赋值

                  len = len - c + w;//长度收缩

                  for(char x=w+1;x<len;x++)//合并各位对应数值
                        *(num+x) = *(num+x+c-w);

                  for(x=w;x<len;x++)//合并位
                        *(wei+x) = *(wei+x+c-w);
            }
            w--;
      }
      a = 0;//临时计算用数
      c = 0;
      while(c<len)//计算
      {
            tmp = *(num+c);
            switch(*(wei+c))
            {
            case 10: i = 10;break;
            case 11: i = 20;break;
            case 12: i = 100;break;
            case 13: i = 1000;break;
            case 14: i = 10000;break;
            case 15: i = 1000000;break;
            case 16: i = 100000000;break;
            default : i = 1;
            }
            if(tmp)
                  a = a + i*tmp;
            else
                  if(*(wei+c))
                        a = a + i;//将单位转换为数值
            c++;
      }
      return a;
}

double numch(char *str)
{//转成非负整数
      char wei[MAX]={0};//位数记录
      char len=0;//下标最大值
      double num[MAX]={0};
      char x,k;//临时数据
      char *t = str;//临时指针

      while(len<MAX && *t)//循环,筛选整理
      {
            if(*t<='9' && *t>='0')//若是阿拉伯数字字符
            {
                  x = *t - '0';//计算
                  t--;//阿拉伯数字只占一个字节
            }
            else
            {
                  x = 0;
                  while(x<29 && (*t != *(_NUMBER+x*2) || *(t+1) != *(_NUMBER+x*2+1)))
                        x++;//检测比较中文字符

                  if(x==29)
                        break;//遇到非数字字符

                  if(x<21)
                        x = (x + 1) / 2;//0~9、十,代号10
                  else if(x==21)
                        x = 11;//廿,代号11
                  else if(x<26)
                        x = (x + 2) / 2;//百、千,代号12、13
                  else
                        x = x - 12;//万、兆、亿,代号14、15、16
            }
            if(x>=10)//遇到位代号记录位
            {
                  *(wei+len)=x;//直接使用10~16作代号
                  len++;
                  k = 1;//k==1时关注如“二百五”==250的情况
            }
            else//遇到数字记录数字
            {
                  if(!x)
                        k = 0;//遇到数字为0取消对“二百五”情况的关注
                  *(num+len) = *(num+len) * 10 + x;//每个位对应数字可以大于10
            }

            t += 2;//每个汉字占两个字节(阿拉伯数字已预先减1)
            while(*t==' ')//其他处理,消除空格
                  t++;
            if(*t ==',')//其他处理,遇到一个逗号
                  t++;
      }

      if(t == str)
            return -1;//第一字符非数字字符

      if(!len)//无单位类型返回
            return num[0];

      if(num[len])//不以0结尾
      {
            if(k)//关注“二百五”类型
                  if(!wei[len])//不以非“个”单位结尾
                  {
                        if(wei[len-1]>12)//“千”及以上单位
                              *(wei+len) = *(wei+len-1) - 1;//单位降1
                        else
                              if(*(wei+len-1) == 12)//单位“百”降一位是“十”,11是“廿”的代号
                                    *(wei+len) = 10;
                  }
            len ++;//凡不以0结尾,计算位数要加1
      }

      return _Num(num,wei,len);//进入递归环节
}

//下面验证一下:
#include "stdio.h"
void main()
{
      printf("一二三四五六七八九十>>%.0f\n",numch("一二三四五六七八九十"));
      printf("壹贰叁肆伍陆柒捌玖拾>>%.0f\n",numch("壹贰叁肆伍陆柒捌玖拾"));
      printf("1,2 3,4 5,6 7,8 9,0 >>%.0f\n",numch("1,2 3,4 5,6 7,8 9,0 "));
      printf("一贰三肆五陆七捌九拾>>%.0f\n",numch("一贰三肆五陆七捌九拾"));
      printf("1 贰3,肆 5陆,7捌 9拾>>%.0f\n",numch("1 贰3,肆 5陆,7捌 9拾"));
      printf("5十万3百>>%.0f\n",numch("5十万3百"));
      printf("1 六>>%.0f\n",numch("1 六"));
      printf("1,000>>%.0f\n",numch("1,000"));
      printf("一>>%.0f\n",numch("一"));
      printf("贰>>%.0f\n",numch("贰"));
      printf("百>>%.0f\n",numch("百"));
      printf("十万>>%.0f\n",numch("十万"));
      printf("九亿五百万肆千>>%.0f\n",numch("九亿五百万肆千"));
      printf("2兆廿一>>%.0f\n",numch("2兆廿一"));
      printf("2佰伍>>%.0f\n",numch("2佰伍"));
      printf("2佰0伍>>%.0f\n",numch("2佰0伍"));

      while(getchar()!='\n');
}

[ 本帖最后由 LuoJun_LZ 于 2009-12-24 17:06 编辑 ]
您需要登录后才可以回帖 登录 | 加入易码

本版积分规则

Archiver|手机版|小黑屋|EMAX Studio

GMT+8, 2024-4-20 12:09 , Processed in 0.010170 second(s), 18 queries .

Powered by Discuz! X3.4

© 2001-2017 Comsenz Inc.

快速回复 返回顶部 返回列表