本文目标:

  • 了解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。

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

// 假设当前系统页为CP936(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: 研究简体繁体转换 }

参考文章

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

Delphi与字符编码(实战篇)(MultiByteToWideChar会返回转换后的宽字符串长度)的更多相关文章

  1. 字符编码和python使用encode,decode转换utf-8, gbk, gb2312

    ASCII码 标准ASCII码使用7位二进制数表示大写或小写字母,数字0到9标点符号以及在美式英语中使用的特殊控制字符. 在标准ASCII码中,最高位(b7)用作奇偶校验位,所谓奇偶校验,是指在代码传 ...

  2. 关于Unicode,字符集,字符编码,每个程序员都应该知道的事

    关于Unicode,字符集,字符编码,每个程序员都应该知道的事 作者:Jack47 李笑来的文章如何判断一个人是否聪明?中提到: 必要.清晰.且准确的概念,是一切思考的基石.所谓思考,很大程度上,就是 ...

  3. 关于Unicode,字符集,字符编码

    基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支 ...

  4. 字符编码之间的相互转换 UTF8与GBK(转载)

    转载自http://www.cnblogs.com/azraelly/archive/2012/06/21/2558360.html UTF8与GBK字符编码之间的相互转换 C++ UTF8编码转换 ...

  5. 【miscellaneous】【C/C++语言】UTF8与GBK字符编码之间的相互转换

    UTF8与GBK字符编码之间的相互转换 C++ UTF8编码转换 CChineseCode 一 预备知识 1,字符:字符是抽象的最小文本单位.它没有固定的形状(可能是一个字形),而且没有值." ...

  6. python标准库之字符编码详解

    codesc官方地址:https://docs.python.org/2/library/codecs.html 相关帮助:http://www.cnblogs.com/huxi/archive/20 ...

  7. JAVA的字符编码及问题

    web开发时,字符编码及有时候也会是一个麻烦的问题,没有经验的话,肯定不知道怎么解决,有一定的经验的话,那还是比较简单的.以下,是我学习过程中总结出来的几种字符编码级问题和其解决的方法 1.文档乱码, ...

  8. 001. Java内存中的字符编码

    Java内存中的字符编码 Unicode字符集及utf-8 .utf-16.utf-32 等字符编码方式 字符集:字符表示的数字集合,元素称为码点或码位: 字符编码:字符实际的储存表示: 码点:一个码 ...

  9. 字符编码(续)---Unicode与ANSI字符串转换以及分辨字符编码形式

    Unicode与ANSI字符串转换 我们使用windows函数MultiByteToWideChar将多字节字符串转换为宽字符字符串,如下: int MultiByteToWideChar( UINT ...

随机推荐

  1. What is an http upgrade?

    HTTP Upgrade is used to indicate a preference or requirement to switch to a different version of HTT ...

  2. keil禁止给uncalled segment分配空间

    把target options中的device页中选上"Use LX51 ...",然后在LX51 Misc页中的Misc Control中填入"REMOVEUNUSED ...

  3. WPF制作的一个小功能,智能提示(IntelliSense)

    原文http://www.cnblogs.com/scheshan/archive/2012/06/30/2570867.html 最近WPF项目中遇到一个需求,需要给一个RichTextBox添加智 ...

  4. UESTC_方老师买表 CDOJ 885

    老师买表 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Stat ...

  5. OpenWrt 学习网址

    http://m.blog.csdn.net/blog/woods2001/8137755

  6. 高效CSS書寫規範及CSS兼容性

    一.選擇器針對性說明 某一元素的多个规则集中,选择器的针对性越高,该规则集的权重也就越高.针对性相同的,后出现的规则集的权重更高. * {} /* a=0 b=0 c=0 d=0 -> spec ...

  7. SQL With(递归 CTE 查询)

    本文来自:http://www.cnblogs.com/smailxiaobai/archive/2012/01/16/2323291.html 指定临时命名的结果集,这些结果集称为公用表表达式 (C ...

  8. iOS UIScrollView的简单使用

    本文代码下载 http://vdisk.weibo.com/s/BDn59yfnBVMAJ // // ViewController.m // ScrollView_T1119 // // Creat ...

  9. java学习笔记day04

    1.static关键字  特点:1)随着类的加载而加载        2)优先于对象存在        3)被所有对象所共享        4)可以直接被类名调用(类名.静态成员) 注意:静态方法只能 ...

  10. 【计算几何初步-凸包-Graham扫描法-极角序】【HDU1348】 WALL

    Wall Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submi ...