在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
0. 导言看完“类型。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。为了说明字符和字节的区别,我们来看一个计算字符个数的例子:
// 假设当前系统页为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. 字符编码的检测与转换“工欲善其事,必先利其器”,我先向大家推荐一些工具:
获得不同编码类型的BOM:
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; 检测字符编码类型:
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: 研究简体繁体转换 } 参考文章 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论