Преобразовать функцию в Delphi 2009/2010 (Unicode)

Я постепенно преобразовываю свой существующий код в Delphi 2010 и читаю несколько статей на веб-сайте Embarcaedro, а также технический документ Marco Cantú.

Есть еще некоторые вещи, которые я не понял, поэтому вот две функции, иллюстрирующие мой вопрос:

function RemoveSpace(InStr: string): string;
var
  Ans     : string;
  I       : Word;
  L       : Word;
  TestChar: string[1];
begin
  Ans := '';
  L := Length(InStr);
  if L > 0 then
  begin
    for I := 1 to L do
    begin
      TestChar := Copy(InStr, I, 1);
      if TestChar <> ' ' then Ans := Ans + TestChar;
    end;
  end;
  RemoveSpace := Ans;
end;

function ReplaceStr(const S, Srch, Replace: string): string;
var
  I: Integer;
  Source: string;
begin
  Source := S;
  Result := '';
  repeat
    I := Pos(Srch, Source);
    if I > 0 then begin
      Result := Result + Copy(Source, 1, I - 1) + Replace;
      Source := Copy(Source, I + Length(Srch), MaxInt);
    end
    else Result := Result + Source;
  until I <= 0;
end;

Для функции RemoveSpace, если не передан символ Юникода (например, "aa bb"), все в порядке. Теперь, если я передам текст "ab cd", то функция не будет работать должным образом (я получаю ab??cd в качестве вывода).

Как я могу учесть возможные символы Юникода в строке? использование Length(InStr), очевидно, неверно, так же как и Copy(InStr, I, 1).

Каков наилучший способ преобразования этого кода, чтобы он учитывал символы Юникода?

Спасибо!

5 ответов

Решение

Если это были ваши РЕАЛЬНЫЕ функции, и вы просто пытаетесь заставить их работать, то:

function RemoveSpace(const InStr: string): string;
begin
  Result := StringReplace(InStr, ' ', '', [rfReplaceAll]); 
end;

function ReplaceStr(const S, Srch, Replace: string): string;
begin
  Result := StringReplace(S, Srch, Replace, [rfReplaceAll, rfIgnoreCase]); 
end;

(на данный момент мы не используем D10, так что будьте осторожны!)

Проблема в Delphi заключается в строковых литералах, которые содержат символы вне базового диапазона ascii. Когда они передаются в строковые процедуры, не-ascii-символы заменяются знаками вопроса.

Чтобы избежать этого, приведите текстовые литералы к WideStrings перед передачей их в качестве параметра функции.

Я не знаю, относится ли это к подпрограмме StringReplace, но процедура поиска Delphi Pos/Posex не обрабатывает Unicode правильно. Нам пришлось заменить эти процедуры на наш собственный вариант. Для этой улучшенной подпрограммы важно убедиться, что параметры имеют тип WideString, а не тип обычной строки.

Мы сделали это в D7 при работе с Unicode, и все работает хорошо.

Хотя string теперь это тип Unicode, когда вы указываете длину, вы все равно получаете не-Unicode ShortString тип. TestChar переменная в вашем RemoveSpace Функция - это не-Unicode односимвольная строка. То, что вы должны были использовать все это реально Char переменная. Я ожидаю, что вы пришли из мира VB, где односимвольные строки были такими же, как одиночные символы. В Delphi строка не совпадает с символом, поэтому при вызове Copyвы получите строку.

В Unicode Delphi эта односимвольная строка преобразуется в не-Unicode строку, и если в текущей кодовой странице нет представления для этого символа, вместо этого вы получите знак вопроса. Исправьте это так:

function RemoveSpace(const InStr: string): string;
var
  I: Integer;
  TestChar: Char;
begin
  Result := '';
  for I := 1 to Length(InStr) do
  begin
    TestChar := InStr[I];
    if TestChar <> ' ' then
      Result := Result + TestChar;
  end;
end;

Я избавился от Ans, Начиная с Turbo Pascal 7, вы можете использовать неявно объявленный Result переменная вместо того, чтобы объявлять свою собственную, а затем присваивать ее имени функции. Result читабельно и доступно для записи. Кроме того, вам не нужно беспокоиться о вводе нулевой длины. Когда верхняя граница цикла "for-to" меньше нижней границы, цикл просто не запускается, поэтому вам не нужно проверять заранее. Наконец, я использовал скобочные операторы на InStr извлечь символ по заданному индексу вместо получения строки длиной в один символ.

Вы говорите, что ваше использование Length а также Copy очевидно неверны, но вы ошибаетесь. Эти функции продолжают прекрасно работать в Unicode. Они знают что Char шириной в два байта, так что если вы вызываете их UnicodeString переменные, вы получите правильные символы. Они также продолжают работать над AnsiString переменные. На самом деле, они также работают найти на WideString переменные, даже в старых версиях Delphi.

Основная проблема в вашем коде заключалась в том, что вы сохранили символ Unicode в строковом типе, отличном от Unicode.

Строка [1] не имеет версии Unicode

вместо этого попробуйте Чар.

Судя по описанию вашей проблемы, вы, кажется, обрабатываете строки в кодировке UTF8. Это почти всегда плохая идея. Сначала расшифруйте их в более разумное представление, а затем оперируйте ими. Когда вы закончите, вы можете снова закодировать все как UTF-8.

Я думаю, что тип данных для строк широких символов - "WString" в Delphi; не могу посмотреть прямо сейчас.

Другие вопросы по тегам