分享

UnicodeinRADStudio

 quasiceo 2013-01-11
经过黄叉叉的唆使,我也在家里装上了Delphi2010,一般情况下编译问题不大,但是好多从以前的工程转过来的项目上就有些问题了,经常性的问题就是Unicode的问题!于是网络Google一番,在Delphi的官方站点上发现了Unicode的一些说明,于是大致的翻译记录一下,原文地址:http://docwiki./RADStudio/en/Unicode_in_RAD_Studio
在RADStudio中,基于ANSI-字符串改为基于Unicode字符串:现在的默认情况下,string类型是一个Unicode字符串(UnicodeString)。这个主题将会告诉你如何更好的去处理字符串。
RADStudio现在完全支持Unicode标准,但是在您的某些涉及到字符串操作部分的代码将会需要一些变动,虽然已经花了很多功夫以使用户进行最低限度的变化!这是新的数据类型的介绍,现有的数据类型和功能仍和以往一样。根据公司内部的Unicode转换的经验,现有的开发应用程序应该比较顺利迁移。
现有的String类型
预先存在的数据类型AnsiStringWideString功能与以前一样的方式。
注意:ShortString最多包含255个字符,字符为单字节数据,且只有一个字符计数,不包含编码页(codepage)信息。对于特定应用,ShortString可以包含UTF-8数据,但缺乏普遍性。
此前系统的string实质上是代表AnsiString.下表是之前AnsiString各字段含义的内存表示格式:
引用计数位置字符长度位置字符数据开始位置
控制符结尾
偏移-8偏移-4偏移0长度

在RADStudio中,AnsiString格式已经发生改变,新加入了两个字段(fields),分别是“编码页”字段和“元素大小”字段(CodePageandElemSize)。从而使得AnsiString表示格式与新的UnicodeString类型完全兼容。
WideString
WideString之前用于Unicode字符数据。其格式本质上与WindowsBSTR完全一样。WideString仍旧适用于COM应用。
新String类型:UnicodeString
在RADStudio中,新的默认string类型是UnicodeString类型。
在Delphi中,Char和Pchar类型分别为WideChar和PwideChar。
注意:2009之前的版本,string代表AnsiString,而Char和Pchar类型分别为AnsiChar和PansiChar,此一区别,需要特别注意。
在C++中,_TCHAR映射到一个可选的浮动兴致的定义_TCHAR可以作为wchart_t也可以是Char
VCL中限制使用的是UnicodeString类型,它不再是单字节或MBCS字符串的字符串值。
现在的UnicodeString的内存存储格式为:
代码页位置元素尺寸位置代码页位置长度位置字符数据开始位置NullTerm
-12-10-8-40长度*元素尺寸
UnicodeString可用下面的ObjectPascal结构来表示:
UnicodeinRADStudio UnicodeinRADStudio 代码typeStrRec=record
CodePage:Word;
ElemSize:Word;
refCount:Integer;
Len:Integer;
caseIntegerof
1:array[0..0]ofAnsiChar;
2:array[0..0]ofWideChar;
end;
UnicodeString加入了“编码页”和“元素大小”二字段来说明string内容。UnicodeString与所有其它string类型赋值兼容(isassignmentcompatiblewithallotherstringtypes)。但是,在AnsiString和UnicodeString之间的赋值仍然会出现“往上转换”或“朝下转换”(stilldotheappropriateupordownconversions)。应注意,不建议将UnicodeString类型赋值给AnsiString类型,这可能导致数据丢失。
还应注意AnsiString也包含了CodePage和ElemSize二字段。
UnicodeString在UTF-16里边,有如下理由:
UTF-16与底层操作系统格式匹配。
UTF-16减少了显式/隐式的转换。
调用WindowsAPI时的性能更高。
操作系统不需与UTF-16作任何转换。
基本多语言平台(BMP)已包含了目前世界上绝大多数流行语言文字字形,且适合于单个UTF-16字符(16bits)。
Unicode“替换对”(surrogatepairs)与多字节字符集(MBCS)类似,且更可预测,也更标准。
调度COM接口时,UnicodeString能够提供与WideString之间的双向转换。
UTF-16中的字符可以是2或4字节,因此,string中元素的数目不必一定等于其字符数目。如果string中只包含BMP字符,则字串中的字符个数和元素个数一定相等。
采用UnicodeString有如下好处:

  • 可以实现字串中的字符“引用计数”(Itisreference-counted)。
  • 可以解决以前应用中的遗留问题。
  • 使得AnsiString支持编码信息(codepage),从而消除了隐含类型转换中潜在的数据丢失问题。
  • 编译器能够确保数据在改变之前的正确性。

WideString没有引用计数,因此UnicodeString在某些类型的应用中更加灵活、更加高效(WideString更合适COM)。
索引
UnicodeString类型的实例可以寻址字符。寻址以1为基,如同AnsiString一样。参考下述代码:
varC:Char;
S:
string;
begin
...
C:
=S[1];
...
end;
此种情况下,编译器必须确保S中的数据具有适当的格式。编译器产生代码必须确保对字串元素的赋值具有合适的类型,并通过调用UniqueString函数,保证实例的唯一性(即,以“1”为基的引用)。代码中,由于字串可能包含Unicode数据,编译器在寻址字符阵列之前还需调用UniqueString函数。
编译器条件
在Delphi及C++Builder中,可以使用条件编译允许Unicode和非Unicode代码兼容共存。
Delphi
{$IFDEFUNICODE}
C++Builder
#ifdef_DELPHI_STRING_UNICODE
变化摘要

  • String现在代表UnicodeString而不是AnsiString.
  • Char现在代表WideChar(2字节而非1字节),且是UTF-16字符。
  • Pchar现在表示PWideChar.
  • C++中,System::String现在代表UnicodeString类。
未变化摘要

  • AnsiString.
  • WideString.
  • AnsiChar,PAnsiChar.
  • WideChar,PWideChar
  • 仍可使用隐含类型转换。
  • AnsiString使用用户活动“编码页”。
与字符大小无关的代码结构
下述操作与字符大小无关:

  • 字符串串联:
+
+
+
Concat(,)

  • 标准string函数
  • 字符串运算:

CompareStr()
CompareText()等

  • FillChar()

    • FillChar(Rect,SizeOf(Rect),#0)
    • FillChar(WndClassEx,SizeOf(TWndClassEx),#0).注意到WndClassEx.cbSize:=SizeOf(TWndClassEx);

  • WindowsAPI

    • API调用默认为其WideString("W")版本。
    • PChar()类型转换仍具有相同语义。


代码//GetModuleFileName例子:
functionModuleFileName(Handle:HMODULE):string;
varBuffer:array[0..MAX_PATH]ofChar;
begin
SetString(Result,Buffer,
GetModuleFileName(Handle,Buffer,Length(Buffer)));
end;
//GetWindowText例子:
functionWindowCaption(Handle:HWND):string;
begin
SetLength(Result,
1024);
SetLength(Result,
GetWindowText(Handle,PChar(Result),Length(Result)));
end;
//String字符索引举例:
functionStripHotKeys(constS:string):string;
varI,J:Integer;
LastChar:Char;
begin
SetLength(Result,Length(S));
J:
=0;
LastChar:
=#0;
forI:=1toLength(S)do
begin
if(S[I]'&')or(LastChar='&')then
begin
Inc(J);
Result[J]:
=S[I];
end;
LastChar:
=S[I];
end;
SetLength(Result,J);
end;
与“字符大小”相关的代码结构
在些操作确实与字符大小相关。下面函数及特征列表中也包含了可能的“可移植”版本。可依此重写自己的代码,以便于移植,也就是使得你的代码在AnsiString和UnicodeString变量中都能够正常运行。
SizeOf()–改用可移植指令Length().
Move(...CharCount)–改用可移植的Move(...CharCount*SizeOf(Char)).
StreamRead/Write--改用可移植的AnsiString,SizeOf(Char)或Tencoding类。
FillChar(,,)--改用*SizeOf(Char)(填充#0时),或用StringOfChar函数。
GetProcAddress(,)–改用提供的重载函数并改为PWideChar.
使用类型转换或Pchar作指针运算—在文件顶端加入{IFDEFPByte=PChar}(用Pchar作指针运算时时)。或用{POINTERMATH}编译指令,对所有的类型指针打开(即设为ON),以便按照“元素大小”来增/减量(increment/decrement)。
字符集合结构
可能需要修改的结构:

  • in--代码生成正确(但>#255的字符不在集合当中).编译器会提出警告:"WideCharreducedinsetoperations".根据你的代码,你可以安全地关闭警告。或者,你可使用函数CharinSet取而代之。
  • inLeadBytes–这是全局性的LeadBytes集合,用于本地MBCSANSI.UTF-16仍保留了"leadchar"的概念(#$D800-#$DBFF为高替代,#$DC00-#$DFFF为低替代)。想改变之,可使用重载IsLeadChar指令。ANSI版会检验LeadBytes.而WideChar版本只在“高/低替代”(high/lowsurrogate)时作验证。
  • 字符分类—使用TCharacterstatic类。该字符单元提供了一些函数用于分类字符:IsDigit,IsLetter,IsLetterOrDigit,IsSymbol,IsWhiteSpace,IsSurrogatePair,等等。这些都是基于直接来自Unicode.org的表数据的。
当心这些结构
特别注意下列有问题的代码结构
类型转换将类型模糊化:
有疑问的类型转换–产生警告:
直接构造、操作或访问字串内部结构。有些,比如AnsiString,其内部已经变化,因此是不安全的。建议使用StringRefCount,StringCodePage,StringElementSize及其它函数来获取字串信息。
运行时库
重载.对函数PChar,已有对应版本PAnsiChar和PWideChar,建议使用适当函数。
SysUtils.AnsiXXXX函数,如AnsiCompareString:
AnsiStringsunit中的AnsiXXXX函数提供了SysUtils.AnsiXXXX函数相同的功能,但其只适用于AnsiString.AnsiStrings.AnsiXXXX对AnsiString能够给出比SysUtils.AnsiXXXX函数更好的性能,其原因是它们不进行隐含转换操作。而后者同时适用于AnsiString和UnicodeString。
Write/Writeln和Read/Readln
继续转换到/从ANSI/OEM代码页。
控制台是主要的ANSIorOEM
为以前的应用程序提供更好的兼容性。
TFDD(文本文件设备驱动程序):
TTextRecTFileRec.
文件名是WideChar,但是同上所说,数据是ANSI/OEM.
PByte–用$POINTERMATHON声明。允许阵列寻址及指针运算,如同PAnsiChar.
字串信息函数Stringinformationfunctions:
StringElementSize返回实际数据的大小。
StringCodePage返回的字符串数据的代码页。
StringRefCount返回的引用计数。
RTL提供了帮助函数,可帮助在“编码页”和“元素大小”之间进行显式的转换。通常开发人员在字符阵列上使用MOVE函数时,并不知道元素大小。但若能确认所有的RValue引用都产生了对RTL的正确调用从而保证了合适的元素大小,问题就得以缓解。
组件和类

  • TStrings:内部保存有UnicodeString(仍需声明为string).
  • TWideStrings(可能被弃用)未改变。在内部使用WideString(BSTR)。
  • TStringStream
已被重写-默认为默认ANSI编码的内部存储。
Encodingcanbeoverridden.
考虑使用TStringBuilder替代TStringStream去从bitsandpieces来构造一个字符串

  • TEncoding
Defaultstousers’activecodepage.
支持UTF-8.
支持UTF-16,UTF-16be,UTF-16le
ByteOrderMark(BOM)support.
Youcancreatedescendentclassesforuser-specificencodings.(您可以继承该类创建自己的特定编码)

  • 组件流(即DFM文本文件)
完全向后兼容
StreamasUTF-8onlyifcomponenttype,propertyornamecontainsnon-ASCII-7characters.
Stringpropertyvaluesarestillstreamedin“#”escapedformat.
MayallowvaluesasUTF-8aswell(openissue).
OnlychangeinbinaryformatispotentialforUTF-8dataforcomponentname,properties,andtypename.
字节顺序标志(ByteOrderMark)
就在文件中加入“字节顺序标志”(BOM)以表示其编码:

  • UTF-8使用EFBBBF.
  • UTF-16小端:使用FFFE.
  • UTF-16大端:使用FEFF.

StepstoUnicode-enableyourapplications
用户需要进行如下步骤:

  • 检查(所有)char-和string-相关函数.
  • 重建应用.
  • 检查替代符号对(Reviewsurrogatepairs).
  • 检查string有效性/安全性(payloads).

新增Delphi编译警告
NewwarningshavebeenaddedtotheDelphi编译程序新增了类型转换(如由UnicodeString或WideString朝下转换成AnsiString或AnsiChar)相关的可能错误警告。当将应用转换到Unicode时,应该使能警告1057和1058,以支持在代码中发现问题区域。

  • 1057隐含从'%s'到'%s'的类型转换(IMPLICIT_STRING_CAST),当编译程序检测到必须将AnsiString(或AnsiChar)隐式地转换为某种Unicode(UnicodeString字串或WideString字串)形式时发出该警告。(注意:该警告最终将会被默认使能).
  • 1058从'%s'到'%s'隐含字串类型转换可能有数据丢失。(IMPLICIT_STRING_CAST_LOSS)当编译程序检测到必须将某种Unicode(UnicodeString字串或WideString字串)形式隐式地转换为AnsiString(或AnsiChar)字串时发出该警告。这是一种潜在的损失性转换,这是因为有些字符,无法在目标串的编码页中表示出来。(注意:注意:该警告最终将会被默认使能).
  • 1059:略
  • 1060:略


  • 保持源文件为UTF-8格式.
  • 当代码必须是AnsiString或AnsiChar时,在IDE中进行重构(refactoring)。(代码仍旧可移植codeisstillportable).
  • Static代码检查:
  • 留意所有警告(乃至错误):
  • 确认代码意图

建议
保存源文件中的UTF-8格式:
Delphi2005,2006,2007都支持.
文件仍然能够以Ansi的形式编译(可以使用codepage编译器开关).
WriteaUTF-8BOMtosourcefile.Makesureyoursourcecontrolmanagementsystemsupportsthesefiles(mostdo).
PerformIDErefactoringwhencodemustbeAnsiStringorAnsiChar(codeisstillportable).
静态代码审查:

  • Iscodemerelypassingthedataalong?
  • Iscodedoingsimplecharacterindexing?
注意所有的警告(elevatetoerrors):

  • 可疑的指针类型转换。
  • 隐显视转换
明确代码的目的

  • 代码是否将字符串作为一个动态数组使用,如果是,使用TBytes类型代替它
  • 一个Pchar能否被转换成Pointer进行指针运算?如果可以转换成PByte类型,替代它,同时打开编译器开关$POINTERMATHON

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多