将包含转义组合变音符号(如“Fu\u0308rst”)的 Delphi XE AnsiString 转换为友好的 WideString“Fürst”的最佳方法是什么?
我知道这并不总是适用于所有组合,但是应该支持常见的拉丁语块,而无需自己构建愚蠢的转换表。我想可以在新的 Characters 单元的某个地方找到解决方案,但我不明白。
将包含转义组合变音符号(如“Fu\u0308rst”)的 Delphi XE AnsiString 转换为友好的 WideString“Fürst”的最佳方法是什么?
我知道这并不总是适用于所有组合,但是应该支持常见的拉丁语块,而无需自己构建愚蠢的转换表。我想可以在新的 Characters 单元的某个地方找到解决方案,但我不明白。
我认为您需要执行Unicode 规范化。在你的弦上。
我不知道 Delphi XE RTL 中是否有特定的调用来执行此操作,但 WinAPI 调用NormalizeString应该可以帮助您,使用模式 NormalizationKC:
归一化KC
Unicode 规范化形式 KC,兼容性组合。将每个基本字符和组合字符转换为规范的预组合等效字符,并将所有兼容性字符转换为其等效字符。例如,连字 fi 变成 f + i;类似地,A + ¨ + fi + n 变为 Ä + f + i + n。
这是解决我的问题的完整代码:
函数 Unescape(const s: AnsiString): 字符串;
变量
i:整数;
j:整数;
c:整数;
开始
// 使结果至少足够大。这可以防止过多的重新分配
SetLength(结果,长度);
我:= 1;
j := 1;
而 i <= 长度确实开始
如果 s[i] = '\' 然后开始
如果 i < Length(s) 然后开始
// 转义反斜杠?
如果 s[i + 1] = '\' 然后开始
结果[j] := '\';
公司(我,2);
结尾
// 将十六进制数转换为 WideChar
else if (s[i + 1] = 'u') and (i + 1 + 4 <= Length(s))
然后 TryStrToInt('$' + string(Copy(s, i + 2, 4)), c) 开始
公司(我,6);
结果[j] := WideChar(c);
结束否则开始
raise Exception.CreateFmt('位置 %d 处的无效代码', [i]);
结尾;
结束否则开始
raise Exception.Create('字符串意外结束');
结尾;
结束否则开始
结果[j] := WideChar(s[i]);
公司(一);
结尾;
公司(j);
结尾;
// 如果我们保留了太多空间,则修剪结果
设置长度(结果,j - 1);
结尾;
常量
归一化C = 1;
函数 NormalizeString(NormForm:整数;lpSrcString:LPCWSTR;cwSrcLength:整数;
lpDstString:LPWSTR;cwDstLength:整数):整数;标准调用;外部“Normaliz.dll”;
函数 Normalize(const s: string): string;
变量
新长度:整数;
开始
// 在 NormalizationC 模式下,结果字符串不会比输入字符串长
SetLength(结果,长度);
newLength := NormalizeString(NormalizationC, PChar(s), Length(s), PChar(Result), Length(Result));
设置长度(结果,新长度);
结尾;
函数 UnescapeAndNormalize(const s: AnsiString): 字符串;
开始
结果 := Normalize(Unescape(s));
结尾;
谢谢你们!我确信我对 StackOverflow 的第一次体验不会是我的最后一次 :-)
他们总是这样逃跑吗?总是4位数?
\ 字符本身是如何转义的?
假设 \character 被 \xxxx 转义,其中 xxxx 是 \ 字符的代码,您可以轻松地遍历字符串:
function Unescape(s: AnsiString): WideString;
var
i: Integer;
j: Integer;
c: Integer;
begin
// Make result at least large enough. This prevents too many reallocs
SetLength(Result, Length(s));
i := 1; j := 1;
while i <= Length(s) do
begin
// If a '\' is found, typecast the following 4 digit integer to widechar
if s[i] = '\' then
begin
if (s[i+1] <> 'u') or not TryStrToInt(Copy(s, i+2, 4), c) then
raise Exception.CreateFmt('Invalid code at position %d', [i]);
Inc(i, 6);
Result[j] := WideChar(c);
end
else
begin
Result[j] := WideChar(s[i]);
Inc(i);
end;
Inc(j);
end;
// Trim result in case we reserved too much space
SetLength(Result, j-1);
end;
像这样使用
MessageBoxW(0, PWideChar(Unescape('\u0252berhaupt')), nil, MB_OK);
此代码在 Delphi 2007 中进行了测试,但由于明确使用了 Ansistring 和 Widestring,它也应该在 XE 中工作。
[编辑] 代码没问题。荧光笔失败。
如果我没记错的话,Delphi XE 现在支持正则表达式。不过,我不经常使用它们,但这似乎是解析字符串然后替换所有转义值的好方法。也许有人有一个很好的例子来说明如何在 Delphi 中使用正则表达式来做到这一点?
GolezTrol,你忘了'$'
if (s[i+1] <> 'u') or not TryStrToInt('$'+Copy(s, i+2, 4), c) then