Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)

本文目标:

  • 了解Delphi的字符串类型
  • 字符编码的检测与转换
  • 简体繁体转换

0. 导言

看完“.Net与字符编码(理论篇)”,我们明白了字符是自然语言中的最小单位,在存储和传输的过程中可以使用三种编码方法:ASCII、DBCS以及Unicode。常见的DBCS编码有GB2312、GBK和BIG5,而UTF-8、UTF-16和UTF-32则是最常用的Unicode编码类型。

1. 字符串类型

在Delphi中有两种字符串类型:AnsiStringWideString。AnsiString被称为“长字符串”(Long String);WideString则叫做“宽字符串”(Unicode String),它和COM String (BSTR)兼容。它们都是由程序在(Heap)上分配的并自动管理内存的分配和释放。目前在Win32平台上,string类型等同于AnsiString。AnsiString还可以理解成字节序列,它支持单字节字符编码(SBCS)、多字节字符编码(MBCS/DBCS)以及UTF-8编码。而WideString使用UTF-16编码,完美支持Unicode。

为了说明字符和字节的区别,我们来看一个计算字符个数的例子:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)// 假设当前系统页为CP936(GBK 1.0)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)procedure TestAnsiLength;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)var
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  str: string;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  str := '汉字ABC';
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Assert(Length(str) = 7);      // 7个字节
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Assert(AnsiLength(str) = 5);  // 5个字符
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;

下面是AnsiLength的两种实现:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)// uses SysUtils;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)function AnsiLength(const s: string): integer;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)var
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  p, q: PChar;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Result := 0;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  p := PChar(s);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  q := p + Length(s);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  while p < q do
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Inc(Result);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    if p^ in LeadBytes then // 当前系统代码页的前导字节数组
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      Inc(p, 2)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    else
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      Inc(p);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)// uses Windows;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)function AnsiLength(const s: string): Integer;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Result := MultiByteToWideChar(CP_ACP, 0, PAnsiChar(s), -1, nil, 0);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  if Result > 0 then Dec(Result);  // 除去终止符
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;

如果理解了.Net与字符编码(理论篇)中的编码知识,上面的例子还是很简单的。

2. 字符编码的检测与转换

“工欲善其事,必先利其器”,我先向大家推荐一些工具:

定义基本的类型:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  { 编码类型 }
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  TEncodingType = (
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etAnsi,       // ANSI   format (SBCS/DBCS)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF8,       // UTF-8  format
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicode,    // UTF-16 format using little endian
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicodeBE,  // UTF-16 format using big endian
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF32,      // UTF-32 format using little endian
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF32BE     // UTF-32 format using big endian
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  );
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  { 字节顺序标记 }
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  TByteOrderMask = array of Byte;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)

获得不同编码类型的BOM:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)CopyBytes
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)function TryGetBOM(const encodingType: TEncodingType; var bom: TByteOrderMask): Boolean;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Result := True;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  case encodingType of
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF8:      CopyBytes(BOM_Utf8, bom);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicode:   CopyBytes(BOM_UTF16_LSB, bom);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicodeBE: CopyBytes(BOM_UTF16_MSB, bom);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF32:     CopyBytes(BOM_UTF32_LSB, bom);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF32BE:   CopyBytes(BOM_UTF32_MSB, bom);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    else
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      SetLength(bom, 0);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      Result := False;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)

检测字符编码类型:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)CompareBOM
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)function DetectEncoding(buffer: PAnsiChar): TEncodingType; overload;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  if CompareBOM(buffer, BOM_UTF8) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etUTF8
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  else if CompareBOM(buffer, BOM_UTF16_LSB) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etUnicode
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  else if CompareBOM(buffer, BOM_UTF16_MSB) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etUnicodeBE
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  else if CompareBOM(buffer, BOM_UTF32_LSB) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etUTF32
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  else if CompareBOM(buffer, BOM_UTF32_MSB) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etUTF32BE
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  else
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    Result := etAnsi;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)function DetectEncoding(stream: TStream): TEncodingType; overload;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)var
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  pos: Int64;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  bytes: TByteOrderMask;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  SetLength(bytes, 6);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  ZeroMemory(@bytes[0], Length(bytes));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  pos := stream.Seek(0, soFromCurrent);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  stream.Seek(0, soFromBeginning);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  stream.Read(bytes[0], SizeOf(bytes));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  stream.Seek(pos, soFromBeginning);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  Result := DetectEncoding(PAnsiChar(@bytes[0]));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;

下面的方法演示了如何用不同的编码类型来保存文本:

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)procedure WriteText(stream: TStream; const buffer: WideString;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  const encodingType: TEncodingType; withBom: Boolean = False);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)var
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  s: AnsiString;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  p: PAnsiChar;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  bom: TByteOrderMask;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  bytes: Integer;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  p := nil;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  bytes := Length(buffer) * SizeOf(WideChar);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  if withBom and TryGetBOM(encodingType, bom) then
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    stream.Write(bom[0], Length(bom));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  end;  
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  case encodingType of
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etAnsi:
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      p := PAnsiChar(buffer);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      bytes := Length(buffer);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUTF8:
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      s := Utf8Encode(buffer);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      p := PAnsiChar(s);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      bytes := Length(s);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicode:
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      p := PAnsiChar(PWideChar(buffer));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    etUnicodeBE:
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      StrSwapByteOrder(PWideChar(buffer));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      p := PAnsiChar(PWideChar(buffer));
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    else  // 留给读者去实现
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    begin
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)      raise Exception.Create('Not Implemented.');
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)    end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  end;
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)  stream.Write(p^, bytes);
Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)end;

需要说明的是,如果把这些过程封装成对象的话,结构会更清晰。

3. 简体繁体转换

简体繁体转换包括简转繁繁转简两种情况,其原理是利用查找字符编码映射表来查找相应的字符。网上有一个“利用编码对照表完成内码转换和简繁体转换的单元”就是基于这个原理写的,在这里就暂不详述了。

{ TODO: 采用OOP来封装字符编码模块,并提供下载 }
{ TODO: 研究简体繁体转换 }

参考文章

http://www.cnblogs.com/baoquan/articles/1027371.html

上一篇:SSM基础整合


下一篇:LeetCode 231