中文简繁互转的三种方法

来源:互联网 发布:淘宝如何增加排名 编辑:程序博客网 时间:2024/05/21 00:44

 

中文简繁互转的三种方法

API对照表Unicode

                                                                                                        

 

汉字处理是我们广大程序员所面对的中国特色问题之一,都是用汉语,干嘛非要整个简体繁体,我只能心里暗骂,该死的政治。没办法,抱怨归抱怨,问题还是要解决。本文主要是总结几种现有的中文简繁互转方法,内容大部分来自前人成果,小部分是自己的心得,在此感谢这些我不认识的朋友。

 

目前网络上最流行的转换方式是利用API函数,但是大部分都有或多或少的问题,如:在繁体下无法工作,未对原理进行解释等;本文除了介绍API方式外还将介绍另外两种方式。

中文编码简介

       中文编码目前主要存在以下四种,GB2312GBKBIG5Unicode

      

       GB2312是中华人民共和国国家汉字信息交换用编码,全称《信息交换用汉字编码字符集——基本集》,由国家标准总局发布,198151实施,通行于大陆。新加坡等地也使用此编码。GB2312收录了大部分普通的汉字,共6763字。但是对于古文和人名中出现的罕见字没有收录,而且也没有收录港台地区广泛使用的繁体字。比如,我们的朱镕基总理的“镕”就没在GB2312里,直接导致当初很多报社用“钅容”拼起来代替。正是这个缺点导致GBK的诞生。

      

       GBK编码是中国大陆制订的、等同于UCS的新的中文编码扩展国家标准。GBK工作小组于199510月,同年12月完成GBK规范。该编码标准兼容GB2312,共收录汉字21003个、符号883个,并提供1894个造字码位,简、繁体字融于一库。值得注意的是,GBK自身并非国家标准,只是曾由国家技术监督局标准化司、电子工业部科技与质量监督司公布为技术规范指导性文件

 

       Big5的产生,是因为当时台湾不同厂商各自推出不同的编码,如IBM 5550、王安码等,彼此不能兼容;另一方面,台湾当时尚未推出官方的汉字编码,而中国内地所推行的GB 2312编码,亦未有收录繁体字。在这样的时空背景下,为了使台湾早日进入信息时代,所采行的一个计划;同时,这个计划对于以台湾为核心的亚洲汉字圈也产生了久远的影响。

 

       Unicode(统一码、万国码、单一码)是一种在计算机上使用的字符编码。它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本转换、处理的要求,其中也包含了简体和繁体中文。1990年开始研发,1994年正式公布。

四种中文编码比较

编码集

中文编码方式

中文编码表

GB2312

双字节

高位:A1 F7

低位:A1 – FE

GBK

双字节

高位:81 – FE

低位:40 – 7E   80 – FE

BIG5

双字节

高位:81 – FE

低位:40 7E   A1 – FE

Unicode(UTF-16)

双字节

4E00 – 9FBF

API方式

API方式主要是利用三个windows 函数实现,MultiByteToWideCharWideCharToMultiByteLCMapStringMultiByteToWideCharWideCharToMultiByte是用来实现AnsiUnicode之间的转换。LCMapString是用来实现各种字符集之间转换。

 

由于GBK码包含了简体和繁体,API方式实现中文简繁互转实际上是利用了GBK码作为中介,先转换成GBK码再转成GB2312BIG5

 

简体转繁体

 

GB2312GBK在简体编码上是重合的,所以没必要在转换成GBK,直接使用LCMapString来实现简繁转换。

 

function APIGbToBig5(valStr: string): string;

var

  Len: Integer;

  P: PAnsiChar;

  local: LCID;

begin

  Len := Length(valStr) + 1;

 

  local := MAKELCID(

    MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED),

      SORT_CHINESE_PRCP);

 

  P := GetMemory(Len);

  try

    LCMapString(local, LCMAP_TRADITIONAL_CHINESE, PAnsiChar(valStr),

      Len, P, Len);

 

    Result := P;

  finally

    FreeMemory(P);

  end;

end;

 

繁体转简体:

 

首先将BIG5转换成Unicode,再把Unicode转换成GBK码,最后通过LCMapString实现从简繁转换。

 

function APIBig5ToGB(valStr: string): string;

var

  Len: Integer;

  P: PAnsiChar;

  local: LCID;

begin

  ////big5 - > gbk

  valStr := GBUnicodeToAnsi(BIG5AnsiToUnicode(valStr));

 

  local := MAKELCID(

    MAKELANGID(LANG_CHINESE, SUBLANG_CHINESE_SIMPLIFIED),

      SORT_CHINESE_BIG5);

 

  Len := Length(valStr) + 1;

  P := GetMemory(Len);

  try

    LCMapString(local, LCMAP_SIMPLIFIED_CHINESE, PAnsiChar(valStr),

      Len, P, Len);

 

    Result := P;

  finally

    FreeMemory(P);

  end;

end;

 

对照表方式

对照表方式是通过将所有的中文字符做成一张简繁字符对照表,再根据索引来查找翻译。这种方式简单实用也好理解,速度也还不错,缺点就是这么大张表可得糟蹋大概几十K内存。

 

这种方式看起来首要解决的问题就是这个码表如何建立,码表的建立需要解决两个问题,一个是简繁体的对应关系,第二个是中文字符到索引的映射算法。简繁体对应关系好说,根据GB2312BIG5的编码表可以生成2张包含所有中文字的列表,再通过目前的翻译软件(如:Office自带的简繁互转,Google的在线翻译或者其他的翻译软件)实现简繁转换,这样这张表就生成了。本文的附带源码中有生成工具,实现了此过程。对于索引映射算法,也是根据码表关系生成,下面分别介绍。

 

简体转繁体:

 

索引算法:

 

function Hash(valStr: string): Integer;

begin

  if Length(valStr) = 2 then

  begin

    Result := (Ord(valStr[1]) - $A1)*($FE-$A1+1) + (Ord(valStr[2]) - $A1)

  end else

    Result := -1;

end;

 

转换代码:

 

function AGB2BIG5(valGBString: string): string;

var

  Idx, strLen: Integer;

  G, B: Word;

begin

  Result := '';

  strLen := Length(valGBString);

  Idx := 1;

  while Idx <= strLen do

  begin

    if (valGBString[Idx] >= #$A1) and (valGBString[Idx] <= #$F7) and

      (valGBString[Idx + 1] >= #$A0) and (valGBString[Idx + 1] <= #$FF) then

    begin

      G := Hash(valGBString[Idx] + valGBString[Idx + 1]);

      if (G >= Low(CodeArray)) and (G <= High(CodeArray)) then

        B := CodeArray[G]

      else

        B := G;

 

      Result := Result + Chr(Hi(B)) + Chr(Lo(B));

 

      Inc(Idx, 2);

    end else

    begin

      Result := Result + valGBString[Idx];

      Inc(Idx, 1);

    end;

  end;

end;

 

繁体转简体:

 

索引算法:

 

function Hash(valStr: string): Integer;

var

  loCount, Step: Word;

begin

  Step := $7F - $40;

  loCount := $7F - $40 + $FF - $A1;

  if Length(valStr) = 2 then

  begin

      if (valStr[2] >= #$40) and (valStr[2] <= #$7E) then

        Result := (Ord(valStr[1]) - $A1)*loCount + (Ord(valStr[2]) - $40)

      else

        Result := (Ord(valStr[1]) - $A1)*loCount + Step + (Ord(valStr[2]) - $A1);

  end else

    Result := -1;

end;

 

转换代码:

 

function ABIG52GB(valBig5String: string): string;

var

  Idx, strLen: Integer;

  G, B: Word;

begin

  Result := '';

  strLen := Length(valBig5String);

  Idx := 1;

  while Idx <= strLen do

  begin

    if (valBig5String[Idx] >= #$A1) and (valBig5String[Idx] <= #$F9) and

      (valBig5String[Idx + 1] >= #$40) and (valBig5String[Idx + 1] <= #$FE) then

    begin

      G := Hash(valBig5String[Idx] + valBig5String[Idx + 1]);

      if (G >= Low(CodeArray)) and (G <= High(CodeArray)) then

        B := CodeArray[G]

      else

        B := G;

 

      Result := Result + Chr(Hi(B)) + Chr(Lo(B));

 

      Inc(Idx, 2);

    end else

    begin

      Result := Result + valBig5String[Idx];

      Inc(Idx, 1);

    end;

  end;

end;

 

Unicode方式

Unicode编码中中文的简体繁体统一分布在一个区段中,之间也没有对应关系(实际上也不可能有)。所以这种方式的转换实际上也是通过对照表,只不过这个表的建立有所不同,在Unicode中的中文字,对于简体字和繁体字中重合的这部分字符都是对应同一个编码,所以对照表只需要包含没有重合的那部分字,这样这个表就小了很多,但是新的问题又来了,我上哪儿去弄这些不重合的字符,说实话我也没找到完整的,在维基百科上已经建立了一个项目,专门做这件事情,虽然不是100%完整,但是大部分都包含了。本文的附带源码中有生成工具生成的对照表就是这么生成的,已经能够翻译大部分常见的字符了。

 

简体转繁体:

 

function UGB2BIG5(valGBString: WideString): WideString;

var

  I: Integer;

  G, B: Word;

begin

  Result := valGBString;

  for I := 1 to Length(valGBString) do

  begin

    G := Word(valGBString[I]);

    if (G >= GBFirst) and (G <= GBEnd) then

    begin

      B := CodeArray[G];

      if B = 0 then

        B := G;

      Result[I] := WideChar(B);

    end;

  end;

end;

 

繁体转简体:

 

function UBIG52GB(valGBString: WideString): WideString;

var

  I: Integer;

  G, B: Word;

begin

  Result := valGBString;

  for I := 1 to Length(valGBString) do

  begin

    G := Word(valGBString[I]);

    if (G >= BIG5First) and (G <= BIG5End) then

    begin

      B := CodeArray[G];

      if B = 0 then

        B := G;

      Result[I] := WideChar(B);

    end;

  end;

end;

 

附带源码下载

源码中包含了翻译单元,演示实例和字典生成工具,实例在简体系统繁体系统下均测试通过点击下载

交流

以上为个人心得总结,如有不对之处欢迎指正。邮箱:heroyin@gmail.com

 

原创粉丝点击