Delphi XE AnsiStrings с экранированной комбинацией диакритических знаков - PullRequest
5 голосов
/ 18 ноября 2010

Каков наилучший способ преобразования Delphi XE AnsiString, содержащего экранированные сочетания диакритических меток, таких как «Fu \ u0308rst», в дружественно широкую строку «Fürst»?

Я осознаю тот факт, что это не всегда возможно для всех комбинаций, но общие латинские блоки должны поддерживаться без создания собственных глупых таблиц преобразования самостоятельно.Я думаю, что решение может быть найдено где-то в новом блоке Персонажей, но я не понимаю.

Ответы [ 5 ]

4 голосов
/ 18 ноября 2010

Я думаю, вам нужно выполнить нормализацию Юникода. в вашей строке.

Я не знаю, есть ли конкретный вызов в Delphi XE RTL для этого, но вызов WinAPI NormalizeString должен помочь вам здесь, с режимом NormalizationKC:

NormalizationKC

Форма нормализации Unicode KC, состав совместимости. Трансформации каждая база плюс объединение символов в канонический заранее составленный эквивалент и все символы совместимости с их эквиваленты. Например, лигатура becomes становится f + i; аналогично, A + ¨ + + + n становится Ä + f + i + n.

2 голосов
/ 18 ноября 2010

Вот полный код, который решил мою проблему:

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 не будет моим последним :-)

1 голос
/ 18 ноября 2010

Они всегда так убегали?Всегда в количестве 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, но должен работать и в XE из-за явного использования Ansistring и Widestring.

[править] Код в порядке.Сбой маркера.

0 голосов
/ 08 июля 2013

GolezTrol, вы забыли '$'

if (s[i+1] <> 'u') or not TryStrToInt('$'+Copy(s, i+2, 4), c) then
0 голосов
/ 18 ноября 2010

Если я не ошибаюсь, Delphi XE теперь поддерживает регулярные выражения. Я не так часто их использую, но, похоже, это хороший способ разобрать строку и затем заменить все экранированные значения. Может быть, у кого-то есть хороший пример того, как сделать это в Delphi с помощью регулярных выражений?

...