Delphi XE AnsiStrings с экранированным сочетанием диакритических знаков


Как лучше всего преобразовать Delphi XE AnsiString, содержащий экранированное сочетание диакритических знаков, таких как" Fuu0308rst", в frienly WideString"Fürst"?

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

5 ответов:

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

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

NormalizationKC

Unicode normalization form KC, compatibility composition. Трансформация каждая база плюс комбинирование символов для канонический предварительно составленный эквивалент и все характеры совместимости к их аналог. Например, лигатуры Fi станет ф + я; аналогично, + + фи + N становится Ä + ж + я + н.

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

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

Они всегда так убегают? Всегда в количестве 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.

[edit] код в порядке. Маркер не работает.

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

Голезтрол, вы забываете '$ '

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