5

TStringList で大量のテキスト操作を行う必要があるアプリがあります。基本的に、区切り文字でテキストを分割する必要があります。たとえば、1000 文字の単一行があり、この区切り文字がこの行で 3 回発生する場合、3 行に分割する必要があります。区切り文字には複数の文字を含めることができます。たとえば、'[test]' のようなタグにすることができます。

このタスクを実行するために 2 つの異なるアプローチで 2 つの関数を作成しましたが、どちらも大量のテキスト (通常は 2 メガバイト以上) では遅くなります。

この目標をより速く達成するにはどうすればよいですか?

ここに両方の​​関数があり、どちらも 2 つのパラメーターを受け取ります。元の tstringlist である「lines」と区切り文字である「q」です。

function splitlines(lines : tstringlist; q: string) : integer;
var
  s, aux, ant : string;
  i,j : integer;
  flag : boolean;
  m2 : tstringlist;
begin
  try
    m2 := tstringlist.create;
    m2.BeginUpdate;
    result := 0;
    for i := 0 to lines.count-1 do
    begin
      s := lines[i];
      for j := 1 to length(s) do
      begin
        flag := lowercase(copy(s,j,length(q))) = lowercase(q);
        if flag then
        begin
          inc(result);
          m2.add(aux);
          aux := s[j];
        end
        else
          aux := aux + s[j];
      end;
      m2.add(aux);
      aux := '';
    end;
    m2.EndUpdate;
    lines.text := m2.text;
  finally
    m2.free;
  end;
end;


function splitLines2(lines : tstringlist; q: string) : integer;
var
  aux, p : string;
  i : integer;
  flag : boolean;
begin
  //maux1 and maux2 are already instanced in the parent class
  try
    maux2.text := lines.text;
    p := '';
    i := 0;
    flag := false;
    maux1.BeginUpdate;
    maux2.BeginUpdate;
    while (pos(lowercase(q),lowercase(maux2.text)) > 0) and (i < 5000) do
    begin
      flag := true;
      aux := p+copy(maux2.text,1,pos(lowercase(q),lowercase(maux2.text))-1);
      maux1.add(aux);
      maux2.text := copy(maux2.text,pos(lowercase(q),lowercase(maux2.text)),length(maux2.text));
      p := copy(maux2.text,1,1);
      maux2.text := copy(maux2.text,2,length(maux2.text));
      inc(i);
    end;
  finally
    result := i;
    maux1.EndUpdate;
    maux2.EndUpdate;
    if flag then
    begin
      maux1.add(p+maux2.text);
      lines.text := maux1.text;
    end;
  end;
end;
4

4 に答える 4

16

速度はテストしていませんが、学術的な目的のために、文字列を分割する簡単な方法を次に示します。

myStringList.Text :=
  StringReplace(myStringList.Text, myDelimiter, #13#10, [rfReplaceAll]);
// Use [rfReplaceAll, rfIgnoreCase] if you want to ignore case

Textのプロパティを設定するTStringListと、新しい行で解析され、そこで分割されるため、文字列に変換し、区切り文字を新しい行に置き換えてから、それをTextプロパティに割り当てると機能します。

于 2013-10-24T14:00:14.870 に答える
0

追加オプションとして、正規表現を使用できます。Delphi の最近のバージョン(XE4 および XE5)には、正規表現サポートが組み込まれています。古いバージョンは、 Regular-Expressions.infoで無料の正規表現ライブラリ ダウンロード (zip ファイル)を見つけることができます。

組み込みの正規表現サポートの場合 (ジェネリックを使用TArray<string>):

var
  RegexObj: TRegEx;
  SplitArray: TArray<string>;
begin
  SplitArray := nil;
  try
    RegexObj := TRegEx.Create('\[test\]'); // Your sample expression. Replace with q
    SplitArray := RegexObj.Split(Lines, 0);
  except
    on E: ERegularExpressionError do begin
    // Syntax error in the regular expression
    end;
  end;
  // Use SplitArray
end;

以前のバージョンの Delphi で TPerlRegEx を使用する場合:

var
  Regex: TPerlRegEx;
  m2: TStringList;
begin
  m2 := TStringList.Create;
  try
    Regex := TPerlRegEx.Create;
    try
      Regex.RegEx := '\[test\]';  //  Using your sample expression - replace with q
      Regex.Options := [];
      Regex.State := [preNotEmpty];
      Regex.Subject := Lines.Text;
      Regex.SplitCapture(m2, 0);
    finally
      Regex.Free;
    end;
    // Work with m2
  finally
    m2.Free;
  end;
end;

(ご存じない方のために説明する\と、使用されているサンプル式の は、[]文字が正規表現で意味を持ち、正規表現テキストで使用するためにエスケープする必要があるためです。通常、それらはテキストでは必要ありません。)

于 2013-10-25T13:49:25.850 に答える
0

JCL ライブラリの StrTokens を使用するだけではどうでしょうか。

procedure StrTokens(const S: string; const List: TStrings);

オープンソースです http://sourceforge.net/projects/jcl/

于 2013-10-24T14:00:07.840 に答える