Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - MemoryStream mit Pointern durchforsten


sahib - Di 31.05.05 02:08
Titel: MemoryStream mit Pointern durchforsten
Hi.

Ich habe eine Textdatei in einen TMemoryStream eingelesen und durchforste den Speicherbereich nun nach mir wichtigen Daten. Ich hatte ähnliches schon einmal mit einer TStringList gemacht. Aber das war sehr speicherintensiv und auch nicht sonderlich schnell.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
var
  Stream: TMemoryStream;
  p, q: PChar;  // oder PByte?
begin
  // Zeiger p auf Anfang
  p := Stream.Memory;

  // [...] Suchfunktion à la if p^ = '/' then...
  // q zeigt auf Ende des interessanten Bereichs,
  // als Bsp.:
  q := p + 100;
end;

Jetzt habe ich aber das Problem, den Bereich zwischen p und q in einen String oder PChar (vielleicht besser?) zu bekommen. Wie kann ich das anstellen? Bei mir kommet immer nur das CPU-Fenster :( Ich könnte Zählvariablen mitlaufen lassen und dann mit Move arbeiten. Aber ich müsste doch mit den Pointern selber rechnen können, oder?

Christian


Delete - Di 31.05.05 04:53


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
  p: PChar;
begin
  ms := TMemoryStream.Create;
  try
    ms.LoadFromFile('d:\Dokumente\Träume vom 2004-02-25.txt');
    p := ms.Memory;
    while p^ <> #0 do
    begin
      ShowMessage(p^);
      Inc(p);
    end;
  finally
    FreeAndNil(ms);
  end;
end;

Gibt dir jedes Zeichen einzeln in dem Dialog aus. Nachteil: Du kannst keine AnsiStrings verwenden, da in AnsiStrings auch das #0 Zeichen vorkommen kann, bei PChar's ist es das Stringende-Zeichen.

Erweiterung:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
procedure TForm1.Button1Click(Sender: TObject);
var
  ms: TMemoryStream;
  p, q, r: PChar;
  i: Integer;
  s: string;
begin
  i := 0;
  ms := TMemoryStream.Create;
  try
    ms.LoadFromFile('d:\Dokumente\Träume vom 2004-02-25.txt');
    p := ms.Memory;
    q := p;
    r := p;
    while p^ <> #0 do
    begin
      if p^ = 'd' then
      begin
        // drei Zeichen weiter gehen mit dem einen Zeiger
        Inc(q, 3);
        // einen zurück mit dem anderen Zeiger
        Dec(r, 1);
        // wenn Leerzeichen dahinter und davor
        if (q^ = #32and (r^ = #32then
        begin
          // Zeichenfolge in String kopieren
          SetString(s, p, q - r - 1);
          // auf bestimmten Artikel prüfen
          if (s = 'der'or (s = 'das'or (s = 'die'then
          begin
            ListBox1.Items.Add(s);
            // Zähler erhöhen
            Inc(i);
          end;
        end;
        // Hilfs-Zeiger wieder mit Zeiger p synchronisieren
        Dec(q, 3);
        Inc(r);
      end;
      // alle "Zeichen"-Zeiger eins nach vorne setzen
      Inc(p);
      Inc(q);
      Inc(r);
    end;
  finally
    FreeAndNil(ms);
  end;
  // Anzahl der gefundenen Zeichenfolgen ausgeben
  ShowMessage(IntToStr(i));
end;

Zählt dir alle bestimmten Artikel (der, die, das) in einem Text.

Ich weiß nicht, was du vor hast, aber es wäre zu überlegen, ob du dich nicht mal über Reguläreausdrücke schlau machst.


sahib - Di 31.05.05 09:48

Danke Michael.

Die Funktion SetString fehlte mir, da muss ich mal ein wenig nachlesen. Ich hatte erst mit Move gearbeitet und dabei falsche Daten erhalten. An das schlau machen bezüglich regulärer Ausdrücke dachte ich auch schon, aber das ist ja auch so eine Wissenschaft für sich. Erste Gehversuche meinerseits waren beschämend. Dass sie zweifelsohne mächtiger sind, als selbstgeschriebene Routinen zum auffinden von Texten, ist klar. Wie sieht das aber mit der Geschwindigkeit aus? Das Übersetzen des Ausdrucks nimmt ein wenig Zeit in Anspruch, das sollte aber kein Problem sein, weil der für die Datei gleich bliebe und so nicht in der innersten Schleife verweilte. Die TRegExpr von Andrey V. Sorokin macht einen guten Eindruck.

Christian


deccer - Di 31.05.05 09:55

Move funktioniert ähnlich wie SetString :)


Quelltext
1:
Move(Pointer(sZielString)^, pDeinPointer^, iAnzahlAnBytesOderDieLaengeDieDerStringHabenSoll);                    


oder


Quelltext
1:
2:
SetLength(sZielString, iAnzahlAnBytesOderDieLaengeDieDerStringHabenSoll);
Move(sZielString[1], pDeinPointer^, iAnzahlAnBytesOderDieLaengeDieDerStringHabenSoll);


sahib - Di 31.05.05 10:33

Hallo deccer.

Ersteres Code-Schnipsel sieht ja lecker aus - muss ich mal testen. Ich dachte, dass ich immer wie im zweiten Beispiel vorgehen müsste, nämlich der Initialisierung der Stringlänge. Aber wenn das wie im ersten klappt... ;)

*edit* Ok, reguläre Ausdrücke mit der von mir erwähnten Unit sind viel zu langsam.

Christian