数字转换成中文

把数字转换成中文,在网上能找到很多。找了找发现都不太适合。结果感觉还是直接自己搞个还比找快。实现效率还行100w次在375左右的消耗,分享给大家实现的方法。欢迎多多光临我的小站蘑菇房 moguf.com

基本需求

能把12345 转换成 一万二千三百四十五10转换成,不要一十。最大值不会超过兆。就这么简单。

基本思路

转换分两部分,整数部分和小数部分。小数部分比较简单,直接转换每位小数值都是一一对应。整数部分比较特殊,需要有十百千万亿兆的单位。稍微有些复杂。

简单位数分析
个十百千万十万百万千万亿十亿百亿千亿万亿

上面的单位是有一定规律上去的,到万级后会有两个单位,并且会和前面小的单位结合。如万级会用个十百千作为前部单位,亿比万多一个,依次类推。

按照上面的分析可以得出基本结构数据,最长有三级,第一级为数字,第二级和第三级为单位。

1、中文数字 0~9
2、一级单位 个十百千万亿兆...
3、二级单位 万亿兆...

万开始基本都是按照三位为一个单位,如果我们把前面个到万也看成三位结构,只不过是没有单位的。个位设定为空,没有单位的也设定为空,这样就能统一处理,简化代码实现定义了三组常量 。

1、中文数字 零一二三四五六七八九
2、第一个单位 (空)十百千(空)十百千万(空)十百千万亿(空)十百千万亿兆
3、第二个单位(空空空空)万万万万亿亿亿亿亿兆兆兆兆兆兆兆

第二位单位需要注意的是,每级单位只出现一次。如 11万 = 十一万。因此需要每次加第二个单位时,如果前次已经加过就不用再加。

翻译值存放:

直接放在Char数组中(足够长),数组分为整数部分和小数部分两段。比如共64为,考虑实际情况,整数部分比较长。前50位为整数部分值,后14位为小数部分。

放整数时从50位开始存,向前存,小数的时候从50位置向后存。小数部分位置 - 整数部分位置,就是我们需要的实际长度。

特殊情况处理:
1、零不能连续,如 1001,应该为一千零一。处理:判断前次不为零
2、个位开始的零省略,如1000, 应为 一千。处理:判断第一个填入的数不为零
3、10翻译,应为 十。处理:判断数字是1并且第一位单位是的,一不显示

最终实现代码(Delphi版本)

下面是Delphi 版本代码,在XE3中测试通过。


function FloatToGBNum(AValue: Extended): string;
CONST
  NUMCHAR = '零一二三四五六七八九';
  NUMLEVEL  = ' 十百千 十百千 十百千万 十百千万亿';
  NUMSECTION = '    万万万万亿亿亿亿亿兆兆兆兆兆兆兆';
  INTENDPOS = 50;
  DECIMALCNT = 6; // 取6位小数
var
  rNum: array [0..64] of char;
  cSection:char;
  iIntPos: Integer;
  iDecPos: Integer;
  iNumCnt: Integer;
  iNum: Integer;
  bPrevIsZero: boolean;
  f: TFloatRec;
  i: Integer;

  procedure AddIntChar(const ANum: string; AIdx: Integer);
  begin
    if ANum[AIdx] <> ' ' then
    begin
      dec(iIntPos);
      rNum[iIntPos] := ANum[AIdx];
    end;
  end;

begin
  FloatToDecimal(f, AValue, fvExtended, 20, DECIMALCNT);

  // 整数部分
  bPrevIsZero := false;
  iNumCnt := 0;
  cSection := ' ';
  iIntPos := INTENDPOS;
  for i := f.Exponent - 1 downto 0 do
  begin
    inc(iNumCnt);
    iNum := ord(f.Digits[i]) - ord('0');

    if iNum > 0 then
    begin
      // 二级单位只加一次
      if cSection <> NUMSECTION[iNumCnt] then
      begin
        AddIntChar(NUMSECTION, iNumCnt);
        cSection := NUMSECTION[iNumCnt];
      end;
      AddIntChar(NUMLEVEL, iNumCnt);
      // 一十 不要的
      if not ((iNum = 1) and (NUMLEVEL[iNumCnt] = '十')) then
      begin
        dec(iIntPos);
        rNum[iIntPos] := NUMCHAR[iNum + 1];
      end;
    end
    else  // 零处理
    begin
      if not bPrevIsZero then
      begin
        // 第一个零不加
        if iIntPos <> INTENDPOS then
          AddIntChar(NUMCHAR, 1);
      end;
    end;

    bPrevIsZero := iNum <= 0;
  end;

  if iIntPos = INTENDPOS then
    AddIntChar(NUMCHAR, 1);

  // 小数部分处理
  iDecPos := INTENDPOS;
  if (f.Exponent < 0) or (f.Digits[f.Exponent] <> #0) then
  begin
    rNum[iDecPos] := '点';
    inc(iDecPos);

    if f.Exponent < 0 then
    begin
      iNumCnt := - f.Exponent;
      for i := 1 to iNumCnt do
      begin
        rNum[iDecPos] := NUMCHAR[1];
        inc(iDecPos);
      end;
    end;

    iNumCnt := f.Exponent;
    if iNumCnt < 0 then iNumCnt := 0;
    for i := iNumCnt to High(f.Digits) do
    begin
      if f.Digits[i] = #0 then
        Break;

      iNum := ord(f.Digits[i]) - ord('0');
      rNum[iDecPos] := NUMCHAR[iNum + 1];
      inc(iDecPos);
    end;
  end;

  SetString(Result, PChar(@rNum[iIntPos]), iDecPos - iIntPos);
end;

测试单元代码

unit TestUnit1;

interface

uses
  TestFramework, classes, SysUtils, Unit1, math;

type
  TestTTest = class(TTestCase)
  published
    procedure TestFloatToNum;
  end;

implementation

procedure TestTTest.TestFloatToNum;
begin
  CheckEqualsString(FloatToGBNum(1234567890.01), '十二亿三千四百五十六万七千八百九十点零一');
  CheckEqualsString(FloatToGBNum(1234007890), '十二亿三千四百万零七千八百九十');
  CheckEqualsString(FloatToGBNum(0), '零');
  CheckEqualsString(FloatToGBNum(0.1), '零点一');
  CheckEqualsString(FloatToGBNum(0.101), '零点一零一');
  CheckEqualsString(FloatToGBNum(0.000001), '零点零零零零零一');
  CheckEqualsString(FloatToGBNum(0.0000001), '零点零零零零零零');
  CheckEqualsString(FloatToGBNum(1), '一');
  CheckEqualsString(FloatToGBNum(1.0), '一');
  CheckEqualsString(FloatToGBNum(10), '十');
  CheckEqualsString(FloatToGBNum(1000), '一千');
  CheckEqualsString(FloatToGBNum(1001), '一千零一');
  CheckEqualsString(FloatToGBNum(10000), '一万');
  CheckEqualsString(FloatToGBNum(100000), '十万');
  CheckEqualsString(FloatToGBNum(100000000), '一亿');
  CheckEqualsString(FloatToGBNum(1000000000), '十亿');
  CheckEqualsString(FloatToGBNum(10000000000000), '一兆');
  CheckEqualsString(FloatToGBNum(100000000000010), '十兆零十');
end;

initialization
  RegisterTest(TestTTest.Suite);
end.

相关问题

1、函数有长度限制 2、函数受制于FloatToDecimal函数,更优方法肯定是直接解析float的结构

测试环境:

  • Win7 x64
  • XE3
  • i5 760@2.80GHz
  • 16GB

使用GetTicketCount计数,执行100万次在375左右。函数整体转换效率上没有什么大问题,已经够用。

欢迎多多光临我的小站蘑菇房 moguf.com