本文目标:
为了说明字符和字节的区别,我们来看一个计算 字符个数的例子:
//
假设当前系统页为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与字符编码(理论篇)中的编码知识,上面的例子还是很简单的。
{ 编码类型 }
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
procedure CopyBytes(const source: array of Byte; var target: TByteOrderMask);
var
n: Integer;
s: array of Byte;
begin
n := Length(source);
SetLength(target, n);
if n > 0 then
begin
Move(source[0], target[0], n);
end;
end;
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 CompareBOM(pBuffer: PAnsiChar; const bom: array of Byte): Boolean;
var
i: Integer;
begin
Result := False;
for i := Low(bom) to High(bom) do
begin
Result := (pBuffer^ <> #0) and (PByte(pBuffer)^ = bom[i]);
if not Result then Break;
Inc(pBuffer);
end;
end;
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;
需要说明的是,如果把这些过程封装成对象的话,结构会更清晰。
{ TODO: 采用OOP来封装字符编码模块,并提供下载 }
{ TODO: 研究简体繁体转换 }
参考文章
- 了解Delphi的字符串类型
- 字符编码的检测与转换
- 简体繁体转换
0. 导言
看完“ .Net与字符编码(理论篇)” ,我们明白了字符是自然语言中的最小单位,在存储和传输的过程中可以使用三种编码方法:ASCII、DBCS以及Unicode。 常见的DBCS编码有 GB2312、GBK和BIG5,而UTF-8、UTF-16和UTF-32则是 最常用的Unicode编码 类型。1. 字符串类型
在Delphi中有两种字符串类型: AnsiString和 WideString。AnsiString被称为“长字符串”( Long String);WideString则叫做“宽字符串”( Unicode String),它和COM String ( BSTR)兼容。它们都是由程序在 堆(Heap)上分配的并自动管理内存的分配和释放。目前在Win32平台上, string类型等同于AnsiString。AnsiString还可以理解成 字节序列,它支持单字节字符编码(SBCS)、多字节字符编码(MBCS/DBCS)以及UTF-8编码。而WideString使用UTF-16编码,完美支持Unicode。为了说明字符和字节的区别,我们来看一个计算 字符个数的例子:









下面是AnsiLength的两种实现:























如果理解了 .Net与字符编码(理论篇)中的编码知识,上面的例子还是很简单的。
2. 字符编码的检测与转换
“工欲善其事,必先利其器”,我先向大家推荐一些工具:- JCL (JEDI Code Library)
- Virtual TreeView
- Tnt Controls or TMS Unicode Component Pack

















获得不同编码类型的BOM:
































检测字符编码类型:












































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










































需要说明的是,如果把这些过程封装成对象的话,结构会更清晰。
3. 简体繁体转换
简体繁体转换包括 简转繁和 繁转简两种情况,其原理是利用查找 字符编码映射表来查找相应的字符。网上有一个“ 利用编码对照表完成内码转换和简繁体转换的单元”就是基于这个原理写的,在这里就暂不详述了。{ TODO: 采用OOP来封装字符编码模块,并提供下载 }
{ TODO: 研究简体繁体转换 }
参考文章