在 Unicode 感知 Delphi 版本中处理 AnsiString 及其十六进制代码
Handling AnsiString and its Hex code in Unicode aware Delphi versions
我是 运行 一个遗留应用程序,构建于 Delphi2007 年,过去我们通过在数据库中存储字符的 2 字节十六进制代码来处理非英语字符。
在阅读时,我们应用 char() 将这些十六进制代码转换为字符串。
字符串转十六进制(保存到数据库之前):
strHex := Format( '%x', [ Byte( strText[ lIndex ] ) shr 4 ] );
DataStr[ lPos ] := strHex[ 1 ];
inc( lPos );
strHex := Format( '%x', [ Byte( strText[ lIndex ] ) and [=11=]F ] );
DataStr[ lPos ] := strHex[ 1 ];
inc( lPos );
//in simple I am saving the Hex code to pcData
十六进制到字符串(从数据库读取后):
strText := strText + Chr( StrToInt('$'+ DataStr[lPos] + DataStr[lPos + 1]))
此代码在移动到 Delphi XE7 后开始中断,其中 string 被视为 UniCode 字符串,我们明确必须将字符串转换为 AnsiString 类型。
正在将以下字符串转换为十六进制
ТуцЕфылАшдеук8311
在 Delphi 2007 年给出:
\D2\F3\F6\C5\F4\FB\EB\C0\F8\E4\E5\F3\EA8311
在 Delphi XE7 中给出:
ABB1522
我想知道修改此代码以便处理遗留数据的最佳方法。
根据评论,您只需将此数据解码为本机 Unicode string
。这样做:
- 从数据库读取编码文本到
string
变量中。
- 将该文本解码为字节数组而不是字符串。您的 Delphi 2007 代码几乎可以直接使用,但它需要写入字节数组而不是字符串。
- 该字节数组采用 ANSI 1251 编码。用
TEncoding.GetString
解码它。您需要使用正确的代码页 Encoding := TEncoding.GetEncoding(1251)
. 创建 TEncoding
class 的实例
首先,生成十六进制字符串的更简单方法是使用 RTL 自己的 BinToHex()
函数而不是编写自己的转换代码,例如:
var
...
s: AnsiString;
DataStr: string;
lPos: Integer;
...
begin
...
s := '...';
BinToHex(PAnsiChar(s), @DataStr[lPos], Length(s));
Inc(lPos, Length(s)*2);
...
end;
然后,您可以使用HexToBin()
来反转它。由于您正在处理编码的 ANSI 数据,您可以声明一个 AnsiString
变量,该变量对所需的代码页编码具有亲和力(在您的情况下,可能是 1251),将十六进制代码直接读入该变量,然后 assign/cast 到正常 String
并让 RTL 为您处理到 Unicode 的转换:
type
Win1251String = type AnsiString(1251);
var
...
tmp: Win1251String;
DataStr, strText: string;
lPos: Integer;
...
begin
...
SetLength(tmp, LengthOfHex div 2);
HexToBin(@DataStr[lPos], PAnsiChar(tmp), Length(tmp));
strText := String(tmp);
...
end;
我是 运行 一个遗留应用程序,构建于 Delphi2007 年,过去我们通过在数据库中存储字符的 2 字节十六进制代码来处理非英语字符。 在阅读时,我们应用 char() 将这些十六进制代码转换为字符串。
字符串转十六进制(保存到数据库之前):
strHex := Format( '%x', [ Byte( strText[ lIndex ] ) shr 4 ] );
DataStr[ lPos ] := strHex[ 1 ];
inc( lPos );
strHex := Format( '%x', [ Byte( strText[ lIndex ] ) and [=11=]F ] );
DataStr[ lPos ] := strHex[ 1 ];
inc( lPos );
//in simple I am saving the Hex code to pcData
十六进制到字符串(从数据库读取后):
strText := strText + Chr( StrToInt('$'+ DataStr[lPos] + DataStr[lPos + 1]))
此代码在移动到 Delphi XE7 后开始中断,其中 string 被视为 UniCode 字符串,我们明确必须将字符串转换为 AnsiString 类型。
正在将以下字符串转换为十六进制
ТуцЕфылАшдеук8311
在 Delphi 2007 年给出:
\D2\F3\F6\C5\F4\FB\EB\C0\F8\E4\E5\F3\EA8311
在 Delphi XE7 中给出:
ABB1522
我想知道修改此代码以便处理遗留数据的最佳方法。
根据评论,您只需将此数据解码为本机 Unicode string
。这样做:
- 从数据库读取编码文本到
string
变量中。 - 将该文本解码为字节数组而不是字符串。您的 Delphi 2007 代码几乎可以直接使用,但它需要写入字节数组而不是字符串。
- 该字节数组采用 ANSI 1251 编码。用
TEncoding.GetString
解码它。您需要使用正确的代码页Encoding := TEncoding.GetEncoding(1251)
. 创建
TEncoding
class 的实例
首先,生成十六进制字符串的更简单方法是使用 RTL 自己的 BinToHex()
函数而不是编写自己的转换代码,例如:
var
...
s: AnsiString;
DataStr: string;
lPos: Integer;
...
begin
...
s := '...';
BinToHex(PAnsiChar(s), @DataStr[lPos], Length(s));
Inc(lPos, Length(s)*2);
...
end;
然后,您可以使用HexToBin()
来反转它。由于您正在处理编码的 ANSI 数据,您可以声明一个 AnsiString
变量,该变量对所需的代码页编码具有亲和力(在您的情况下,可能是 1251),将十六进制代码直接读入该变量,然后 assign/cast 到正常 String
并让 RTL 为您处理到 Unicode 的转换:
type
Win1251String = type AnsiString(1251);
var
...
tmp: Win1251String;
DataStr, strText: string;
lPos: Integer;
...
begin
...
SetLength(tmp, LengthOfHex div 2);
HexToBin(@DataStr[lPos], PAnsiChar(tmp), Length(tmp));
strText := String(tmp);
...
end;