Является ли System.Pos в Delphi ущербным? - PullRequest
0 голосов
/ 13 августа 2010

Проблема

Если текст в TRichEdit выглядит примерно так:

'hello, world'#$D#$A

Тогда следующая процедура отображает TRUE.Однако, когда RichEdit имеет

'test'#$D#$A#$D#$A'test'#$D#$A#$D#$A'test'#$D#$A

, тогда процедура отображает FALSE.Мне кажется, что он имеет недостатки, так как находит запятую, но не переводит строки / переводы строк.Я создал обходной путь, чтобы пройтись по строке и найти то, что я ищу, но мне все еще очень любопытно, почему функция Delphi не работает.Любые идеи?

procedure TForm1.Button1Click(Sender: TObject);
var
   sTmp : String;
begin
   sTmp := RichEdit1.Lines.GetText;
   if ( ( Pos( ',', sTmp )  <> 0 ) or
        ( Pos( '"', sTmp )  <> 0 ) or
        ( Pos( '\n', sTmp ) <> 0 ) or
        ( Pos( '\r', sTmp ) <> 0 ) ) then
      Label1.Caption := 'TRUE'
   else
      Label1.Caption := 'FALSE';
end;

Обходной путь - версия Андреаса (быстрее в зависимости от ввода)

function CheckChars( const sData: String ): Boolean;
var
   pCur : PChar;
begin
   pCur := PChar( sData );

   // Exit at NULL terminator
   while ( pCur^ <> #0 ) do
   begin
      case pCur^ of
         #13, #10, #34, #44 : Exit(true);
      end;
      Inc( pCur );
   end;
end;

Правильное использование

function CheckChars( const sData: String ): Boolean
begin
   if ( ( Pos( #44, sData ) <> 0 ) or
        ( Pos( #34, sData ) <> 0 ) or
        ( Pos( #13, sData ) <> 0 ) or
        ( Pos( #10, sData ) <> 0 ) ) then
      Result := true
   else
      Result := false;
end;

Работает для всех протестированных символов, ярешил не смешивать цитируемые и десятичные символы для удобства чтения.Единственный вопрос сейчас, который быстрее?Я думаю, что мой обходной путь будет быстрее, так как я проверяю каждый символ на соответствие всем тем, что ищу, тогда как при использовании функции System.Pos я выполняю одну и ту же процедуру синтаксического анализа 4 раза.

Решение

После некоторого тестирования, это зависит от того, какие символы вы ищете.Тестирование это с запятой (# 44), расположенной 294k символов в строку длиной 589k.Функция, использующая System.Pos, имеет производительность ~ 390 микросекунд, а оператор case выполняет ~ 700 микросекунд.

ОДНАКО!

Если вы измените символ в строке на перевод строки (# 10), то для System.Pos это займет намного больше времени (~ 2700 микросекунд)) из-за повторных звонков.Оператор case все еще работает ~ 700 микросекунд.

Так что я думаю, что если вы ищете определенного персонажа, то System.Pos определенно является подходящим вариантом, однако если вы ищете несколько (что делает мое приложение), то повторный вызов не требуется, когда выможно просто отсканировать его и использовать оператор case.

Ответы [ 2 ]

6 голосов
/ 13 августа 2010

Я не думаю, что Delphi распознает \ n как новую строку, Pos думает, что вы на самом деле ищете символы "\" и "n".

Попробуйте поискать вместо #13 и #10 (возврат каретки и перевод строки) (в качестве альтернативы вы можете использовать #$D и #$A, что будет эквивалентно шестнадцатеричному значению.)

например,

if ( ( Pos( ',', sTmp )  <> 0 ) or
     ( Pos( '"', sTmp )  <> 0 ) or
     ( Pos( #10, sTmp )  <> 0 ) or
     ( Pos( #13, sTmp )  <> 0 ) ) then

Также считаются строки Delphi, и хотя они всегда заканчиваются на # 0. Нет гарантии, что строка не содержит нулевой символ, что означает, что ваш цикл while может завершиться досрочно.

так что в качестве альтернативы вы можете выполнить цикл для i: = 1 до Length (sTmp) (начиная с 1, поскольку sTmp [0] является счетчиком).

или вы можете построить цикл while как

Индекс: = 1;

While Index < Length(sTmp) do
begin
    case sTmp[Index] of
    etc...
2 голосов
/ 13 августа 2010

(На самом деле это комментарий, но он выглядел бы ужасно как единое целое.)

Обратите внимание, что весь ваш блок

case pCur^ of
 #13 :   // CR
    begin
       Result := true;
       break;
    end;
 #10 :   // LF
    begin
       Result := true;
       break;
   end;
  #34 :   // Quote 
   begin
        Result := true;
       break;
    end;
 #44 :   // Comma
    begin
       Result := true;
       break;
    end;
 end;

можно записать более компактно, отметив, что

  • Result := true; break; в этом случае означает то же самое, что и Result := true; Exit;, которое всегда можно записать Exit(true).

  • Несколько случаев могут быть объединены в один случай, если действия идентичны.

Следовательно, вы можете написать

case pCur^ of
  #13, #10, #34, #44: Exit(true);
end;

Но даже лучше, вся функция может быть написана

function CheckChars(const Str: string): boolean;
const
  INTERESTING_CHARS = [#13, #10, #34, #44];
var
  i: integer;
begin
  result := false;
  for i := 1 to length(Str) do
    if Str[i] in INTERESTING_CHARS then
      Exit(true);
end;
...