Delphi与字符编码(实战篇)

Delphi与字符编码(实战篇)

Posted on 2008-01-05 22:39 保权 阅读(136) 评论(0)   编辑   收藏 所属分类: 字符编码Delphi
本文目标:
  • 了解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。

为了说明字符和字节的区别,我们来看一个计算 字符个数的例子:
//  假设当前系统页为CP950(GBK 1.0)
procedure TestAnsiLength;
var
  str: 
string ;
begin
  str :
=   ' 汉字ABC ' ;
  Assert(Length(str) 
=   7 );       //  7个字节
  Assert(AnsiLength(str)  =   5 );   //  5个字符
end;

下面是AnsiLength的两种实现:
//  uses SysUtils;
function AnsiLength( const  s:  string ): integer;
var
  p, q: PChar;
begin
  Result :
=   0 ;
  p :
=  PChar(s);
  q :
=  p  +  Length(s);
  
while  p  <  q  do
  begin
    Inc(Result);
    
if  p ^   in  LeadBytes then  //  当前系统代码页的前导字节数组
      Inc(p,  2 )
    
else
      Inc(p);
  end;
end;

//  uses Windows;
function AnsiLength( const  s:  string ): Integer;
begin
  Result :
=  MultiByteToWideChar(CP_ACP,  0 , PAnsiChar(s),  - 1 , nil,  0 );
  
if  Result  >   0  then Dec(Result);   //  除去终止符
end;

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

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

“工欲善其事,必先利其器”,我先向大家推荐一些工具:
定义基本的类型:

   { 编码类型 }
  TEncodingType 
=  (
    etAnsi,       
//  ANSI   format (SBCS/DBCS)
    etUTF8,        //  UTF-8  format
    etUnicode,     //  UTF-16 format using little endian
    etUnicodeBE,   //  UTF-16 format using big endian
    etUTF32,       //  UTF-32 format using little endian
    etUTF32BE      //  UTF-32 format using big endian
  );

  
{ 字节顺序标记 }
  TByteOrderMask 
=  array of Byte;

获得不同编码类型的BOM:

CopyBytes


function TryGetBOM(
const  encodingType: TEncodingType; var bom: TByteOrderMask): Boolean;
begin
  Result :
=  True;
  
case  encodingType of
    etUTF8:      CopyBytes(BOM_Utf8, bom);
    etUnicode:   CopyBytes(BOM_UTF16_LSB, bom);
    etUnicodeBE: CopyBytes(BOM_UTF16_MSB, bom);
    etUTF32:     CopyBytes(BOM_UTF32_LSB, bom);
    etUTF32BE:   CopyBytes(BOM_UTF32_MSB, bom);
    
else
    begin
      SetLength(bom, 
0 );
      Result :
=  False;
    end;
  end;
end;

检测字符编码类型:
CompareBOM

function DetectEncoding(buffer: PAnsiChar): TEncodingType; overload;
begin
  
if  CompareBOM(buffer, BOM_UTF8) then
    Result :
=  etUTF8
  
else   if  CompareBOM(buffer, BOM_UTF16_LSB) then
    Result :
=  etUnicode
  
else   if  CompareBOM(buffer, BOM_UTF16_MSB) then
    Result :
=  etUnicodeBE
  
else   if  CompareBOM(buffer, BOM_UTF32_LSB) then
    Result :
=  etUTF32
  
else   if  CompareBOM(buffer, BOM_UTF32_MSB) then
    Result :
=  etUTF32BE
  
else
    Result :
=  etAnsi;
end;

function DetectEncoding(stream: TStream): TEncodingType; overload;
var
  pos: Int64;
  bytes: TByteOrderMask;
begin
  SetLength(bytes, 
6 );
  ZeroMemory(@bytes[
0 ], Length(bytes));
  pos :
=  stream.Seek( 0 , soFromCurrent);
  stream.Seek(
0 , soFromBeginning);
  stream.Read(bytes[
0 ], SizeOf(bytes));
  stream.Seek(pos, soFromBeginning);
  Result :
=  DetectEncoding(PAnsiChar(@bytes[ 0 ]));
end;

下面的方法演示了如何用不同的编码类型来保存文本:
procedure WriteText(stream: TStream;  const  buffer: WideString;
  
const  encodingType: TEncodingType; withBom: Boolean  =  False);
var
  s: AnsiString;
  p: PAnsiChar;
  bom: TByteOrderMask;
  bytes: Integer;
begin
  p :
=  nil;
  bytes :
=  Length(buffer)  *  SizeOf(WideChar);
  
if  withBom and TryGetBOM(encodingType, bom) then
  begin
    stream.Write(bom[
0 ], Length(bom));
  end;  
  
case  encodingType of
    etAnsi:
    begin
      p :
=  PAnsiChar(buffer);
      bytes :
=  Length(buffer);
    end;
    etUTF8:
    begin
      s :
=  Utf8Encode(buffer);
      p :
=  PAnsiChar(s);
      bytes :
=  Length(s);
    end;
    etUnicode:
    begin
      p :
=  PAnsiChar(PWideChar(buffer));
    end;
    etUnicodeBE:
    begin
      StrSwapByteOrder(PWideChar(buffer));
      p :
=  PAnsiChar(PWideChar(buffer));
    end;
    
else  // 留给读者去实现
    begin
      raise Exception.Create(
' Not Implemented. ' );
    end;
  end;
  stream.Write(p
^ , bytes);
end;

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

3. 简体繁体转换

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

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

参考文章
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值