それはかなり簡単に見えます。使用しているデータに対してコードをプロファイリングしないと、確認するのは困難です (これは常に良い考えです。Delphi コードを最適化する必要がある場合は、最初にサンプリング プロファイラで実行して、実際にどこに支出しているかを把握してください)。あなたのすべての時間、)しかし、私が経験に基づいた推測をしなければならなかった場合、私はあなたのボトルネックが次の行にあると推測します:
Txt[Idx] := '0';
型の安全なコピー オン ライト セマンティクスのコンパイラの保証の一部としてstring
、文字列の個々の要素 (文字) へのすべての書き込みには、UniqueString
ルーチンへの隠し呼び出しが含まれます。これにより、他の何か、他の場所が参照を保持している文字列を変更していないことが保証されます。
この特定のケースでは、このルーチンの開始時に文字列を新しく取得し、それが一意であることがわかっているため、これは必要ありません。気をつけていれば、回避する方法があります。
明確かつ明確な警告: 最初に一意の文字列があることを確認せずに、これから説明することを実行しないでください。UniqueString
これを行う最も簡単な方法は、手動で 呼び出すことです。また、ループ中に、この文字列を他の変数に割り当てる可能性のある操作を行わないでください。これを行っている間、通常の文字列として扱われません。 この警告に注意しないと、データが破損する可能性があります。
説明が終わったので、ポインタを使用して文字列の文字に直接アクセスし、次のようにコンパイラの保護を回避できます。
procedure TForm1.btn1Click(Sender: TObject);
var
Txt: String;
Idx: Integer;
Tag: Boolean;
current: PChar; //pointer to a character
begin
Tag := False;
Txt := mem1.Text;
UniqueString(txt); //very important
if length(txt) = 0 then
Exit; //If you don't check this, the next line will raise an AV on a blank string
current := @txt[1];
dec(current); //you need to start before element 1, but the compiler won't let you
//assign to element 0
For Idx := 0 to Length(Txt) - 1 Do
Begin
inc(current); //put this at the top of the loop, to handle Continue cases correctly
If (current^ = '<') Then
Tag := True Else
If (current^ = '>') Then
Begin
Tag := False;
Continue;
end;
If Tag Then Continue;
If (not (current^ in [#10, #13, #32])) Then
current^ := '0';
end;
mem2.Text := Txt;
end;
これは比喩を変えます。文字列を配列としてインデックス化する代わりに、ポインターを先頭として文字列をテープのように扱い、一度に 1 文字ずつ進め、最初から最後までスキャンし、必要に応じてその下の文字を変更します。への冗長な呼び出しはUniqueString
なく、オフセットの繰り返し計算もありません。つまり、これははるかに高速になる可能性があります。
このようなポインターを使用する場合は、十分に注意してください。 コンパイラの安全性チェックは正当な理由で存在し、ポインターの使用はそれらの外に出ます。しかし、場合によっては、コード内の処理を高速化するのに本当に役立つことがあります。繰り返しますが、このようなことを試みる前にプロファイルを作成してください。単にわかっていると考えるのではなく、何が速度を低下させているのかを知っていることを確認してください。実行速度が遅いことが判明した場合は、これを行わないでください。代わりに、実際の問題の解決策を見つけてください。