Delphi XE AnsiStrings与逃避组合变音符号

时间:2010-11-18 12:25:31

标签: delphi unicode delphi-xe diacritics

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

我知道这并不总是适用于所有组合,但应该支持常见的Latin块,而不是自己构建愚蠢的转换表。我想解决方案可以在新的Characters单元中找到,但是我没有得到它。

5 个答案:

答案 0 :(得分:4)

我认为您需要在字符串上执行Unicode Normalization.

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

  

NormalizationKC

     

Unicode规范化形式KC,兼容性组合。变换   每个基地加上组合字符   规范的预组合等价物   和所有兼容性字符   他们的等价物例如,结扎fi变为f + i;类似地,A +¨+ fi + n变为Ä+ f + i + n。

答案 1 :(得分:2)

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

function Unescape(const s: AnsiString): string;
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 s[i] = '\' then begin
      if i < Length(s) then begin
        // escaped backslash?
        if s[i + 1] = '\' then begin
          Result[j] := '\';
          inc(i, 2);
        end
        // convert hex number to WideChar
        else if (s[i + 1] = 'u') and (i + 1 + 4 <= Length(s)) 
                and TryStrToInt('$' + string(Copy(s, i + 2, 4)), c) then begin
          inc(i, 6);
          Result[j] := WideChar(c);
        end else begin
          raise Exception.CreateFmt('Invalid code at position %d', [i]);
        end;
      end else begin
        raise Exception.Create('Unexpected end of string');
      end;
    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;

const
  NormalizationC = 1;

function NormalizeString(NormForm: Integer; lpSrcString: LPCWSTR; cwSrcLength: Integer;
 lpDstString: LPWSTR; cwDstLength: Integer): Integer; stdcall; external 'Normaliz.dll';

function Normalize(const s: string): string;
var
  newLength: integer;
begin
  // in NormalizationC mode the result string won't grow longer than the input string
  SetLength(Result, Length(s));
  newLength := NormalizeString(NormalizationC, PChar(s), Length(s), PChar(Result), Length(Result));
  SetLength(Result, newLength);
end;

function UnescapeAndNormalize(const s: AnsiString): string;
begin
  Result := Normalize(Unescape(s));
end;

谢谢大家!我确信我第一次使用StackOverflow不会是我的最后一次:-)

答案 2 :(得分:1)

他们总是像这样逃脱吗?总是4位数?

\字符本身是如何转义的?

假设\字符由\ 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中运行。

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

GolezTrol, 你忘了'$'

if (s[i+1] <> 'u') or not TryStrToInt('$'+Copy(s, i+2, 4), c) then