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。

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

// 假设当前系统页为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

时间: 2024-12-15 15:36:17

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

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

基本概念 字符[character] 字符代表了字母表中的字符,标点符号和其他的一些符号.在计算机中,文本是由字符组成的. 字符集合[character set] 由一套用于特定用途的字符组成,例如支持西欧语言的字符集合,支持中文的字符集合.字符集合只定义了符号和他们的语意,其实跟计算机没有直接关系. 现实生活中,不同的语系有自己的字符集合,例如藏文有自己的字符集合,汉文有自己的字符集合.到计算机的世界中,也有各种字符集合,例如ASCII字符集合,GB2312字符集合,GBK字符集合.还有一个其

一个字符编码处理小程序(一)

一个字符编码处理小程序(一)以前与他人合作申请了一个汉字输入法编码专利(YXY),现在决定继续在此基础上进行一些开发工作,要将它的编码拆分成前.中.后三个子串,以便作进一步的处理.用户故事可以表达为:作为一个代码的开发人员,需要将YXY编码拆分成前.中.后三个子字符串,以便作进一步的汉字分析处理.下面对照个人开发流程,进行开发工作:一. 计划估计这个任务需要多少开发时间.由于利用业余时间开发,开发时间呈现碎片化的状况:故这里只是估计纯的开发时间,大约需要两周.二. 开发1. 分析需求出入内容:Y

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

Unicode与ANSI字符串转换 我们使用windows函数MultiByteToWideChar将多字节字符串转换为宽字符字符串,如下: int MultiByteToWideChar( UINT uCodePage, DWORD dwFlags, PCSTR pMultiByteStr, int cbMultiByte, PWSTR pWideCharStr, int cchWideChar); uCodePage参数标识了与多字节字符串关联的一个代码页值.dwFlags参数允许我们进行额

Java字符编码的转化问题

概述: 我想字符串的编码问题的确会困扰到非常多开发人员.我近期也是被困扰到了. 问题是这种,我们通过二维码扫描来获得二维码中的信息.可是.我们的二维码的产生过程却是"多样化"的.即在产生二维码的时候是以不同的字符串编码类型进行编码的.比方,GBK.GB2312.UTF-8等等.而这些不同的编码类型会产生不同的字节.在Java中.GBK和GB2312都是1个汉字占2个字节,UTF-8是1个汉字占3个字节.而ISO编码则是1上汉字1个字节.这样一来,我们在扫描二维码的时候就会出现一些&qu

刨根究底字符编码之零——前言

前言 (图片来自网络) 字符编码是计算机世界里最基础.最重要的一个主题之一.不过,在计算机教材中却往往浮光掠影般地草草带过,甚至连一本专门进行深入介绍的著作都找不到(对这一点我一直很困惑,为什么就没有哪位大牛对这个如此基础.重要而又如此容易让人困惑的主题写一本专著予以介绍呢). 而在编程实践中,如果不发扬死磕到底的精神将字符编码问题的来龙去脉.前世今生彻底搞清楚,那么它终将会像幽灵一样挥之不去,导致时不时地被各种与字符编码相关的"灵异"事件折磨得死去活来. 本人正是在经受了字符编码所带

python标准库之字符编码详解

codesc官方地址:https://docs.python.org/2/library/codecs.html 相关帮助:http://www.cnblogs.com/huxi/archive/2010/12/05/1897271.html #python标准库(英文地址:)http://www.ask3.cn/ebook/docspy3zh/library/index.html unicode入门: cpython2.xz支持2种类型字符串处理文本数据,老式的str实例使用单个8位字节表示字

第二章、二进制运算、字符编码、数据类型总结

第二章 二进制运算.字符编码.数据类型 二进制与十进制转换 ASCII表 文字转2进制 计算机容量单位 字符编码 列表 创建: a = list() a = [] 查询: L2[2] 通过索引取值 L2[-1] 通过索引从列表右边开始取值 L2.index('a') 返回指定元素的索引值,从左右查找,找到第一个匹配值,则返回. L2.count('a') 统计指定元素的个数 切片: L2[0:3] 返回从索引0至3的元素,不包括3,顾头不顾尾 L2[0:-1] 返回从索引0至最后一个值,不包括最

宽字符与Unicode (c语言 汉语字符串长度)

在C语言中,我们使用char来定义字符,占用一个字节,最多只能表示128个字符,也就是ASCII码中的字符.计算机起源于美国,char 可以表示所有的英文字符,在以英语为母语的国家完全没有问题. 但是世界上存在很多不同的语言,例如汉语.汉语.日语等有成千上万个字符,需要用多个字节来表示,称之为宽字符(Wide Character).Unicode 是宽字符编码的一种,已经被现代计算机指定为默认的编码方式,Windows 2000以后的操作系统,包括Windows 2000.XP.Vista.Wi

【白话篇】10分钟搞懂字符编码

如上图所示为常见的,让人看了头晕的 几个种编码. 看懂下面几条规则,你就明白他们的关系了. [1]有些人说,GBK严格来说是字符集,而utf-8则是编码,这种区分已经相当模糊了,他们都是"字节到字符的映射关系",所以下面都用编码来说吧. [2] ISO-8859-1 这种编码是单字节编码,衍生于ASCII,表示范围0-255,只要按照ASCII的规则设计的编码,不管是几字节的,都可以和ISO-8859-1兼容. [3]比如说,GBK编码(双字节)能转化成ISO-8859-1编码,是因为