5

将包含转义组合变音符号(如“Fu\u0308rst”)的 Delphi XE AnsiString 转换为友好的 WideString“Fürst”的最佳方法是什么?

我知道这并不总是适用于所有组合,但是应该支持常见的拉丁语块,而无需自己构建愚蠢的转换表。我想可以在新的 Characters 单元的某个地方找到解决方案,但我不明白。

4

5 回答 5

4

我认为您需要执行Unicode 规范化。在你的弦上。

我不知道 Delphi XE RTL 中是否有特定的调用来执行此操作,但 WinAPI 调用NormalizeString应该可以帮助您,使用模式 NormalizationKC:

归一化KC

Unicode 规范化形式 KC,兼容性组合。将每个基本字符和组合字符转换为规范的预组合等效字符,并将所有兼容性字符转换为其等效字符。例如,连字 fi 变成 f + i;类似地,A + ¨ + fi + n 变为 Ä + f + i + n。

于 2010-11-18T12:35:17.357 回答
2

这是解决我的问题的完整代码:

函数 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 的第一次体验不会是我的最后一次 :-)

于 2010-11-18T16:09:49.213 回答
1

他们总是这样逃跑吗?总是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 中工作。

[编辑] 代码没问题。荧光笔失败。

于 2010-11-18T12:45:18.950 回答
0

如果我没记错的话,Delphi XE 现在支持正则表达式。不过,我不经常使用它们,但这似乎是解析字符串然后替换所有转义值的好方法。也许有人有一个很好的例子来说明如何在 Delphi 中使用正则表达式来做到这一点?

于 2010-11-18T15:54:36.203 回答
0

GolezTrol,你忘了'$'

if (s[i+1] <> 'u') or not TryStrToInt('$'+Copy(s, i+2, 4), c) then
于 2013-07-08T19:06:46.907 回答