Entwickler-Ecke

Algorithmen, Optimierung und Assembler - Mehrzeilige Kommentare aus einer Stringliste entfernen


immortuus - Mo 25.07.05 20:44
Titel: Mehrzeilige Kommentare aus einer Stringliste entfernen
volgender textdatei ist gegeben (lade diese in eine Stringliste):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
funktion1(1,true);
{
y := z;
{
showmessage('hallo');
x := 'ist egal';
}

}
for i := 0 to 12 do 
begin
  funktion5(1,'lala');
end;
{
showmessage('noch einer');
x := machwas;
}


das ganze ist delphi syntax. ich habe aber eine eigene "scriptsprache" gebaut. ich suche nun den elegantesten weg die komentare aus der datei zu entfernen.

und bitte nicht mit irgendwelchen tolls oder regex... ich will es selber schreiben nur mit delphi boardmitteln

Moderiert von user profile iconChristian S.: Code- durch Delphi-Tags ersetzt.


maxk - Mo 25.07.05 21:34

Hallo und :welcome: im DF.
Das sollte eigentlich kein Problem sein. Du ziehst dir den gesamten Inhalt in einen String (z.B. Memo1.Lines.Text) und löscht dann alles zwischen { und } raus. Ich habe gerade kein Delphi zur Hand, aber es sollte mittels Suche in: Delphi-Forum, Delphi-Library "WHILE" und Suche in: Delphi-Forum, Delphi-Library "POS" und Suche in: Delphi-Forum, Delphi-Library "DELETE" gehen. Ich schreib nachher nochmal was, falls mir keiner zuvorkommt ;)

Gruß,
maxk

PS: Bitte benutze demnächst die Delphitags für Sourcecodeausschnitte (auch wenn es abgewandelter ist).

// Edit: Folgender Schnipsel entfernt die Kommentare, wenn die Klammern aufgehen. Falls die doppelte Zeile (7+8) kein Versehen ist, muss du allerdings daran noch ein wenig rumpfeilen.

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
function RemoveComments(const Str:string):string;
var p1,p2:integer;
begin
 Result:=Str;
 while pos('{',Result)<pos('}',Result) do begin
  p1:=pos('{',Result);
  p2:=pos('}',Result);

  Delete(Result,p1,p2-p1+1);
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 with Memo1.Lines do Text:=RemoveComments(Text);
end;


immortuus - Di 26.07.05 00:06

keine schlechte idee. aber leider kommt danach das raus:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
funktion1(1,true);   
   
}   
for i := 0 to 12 do    
begin   
  funktion5(1,'lala');   
end;   
{   
showmessage('noch einer'); 
  
x := machwas;   
}


ich habe mir mal gedanken gemacht. ich überlege ob man dan nicht mit einer rekusiven funktion machen kann die wenn die letzte { in einem verschachtelten aufbau erschein rumdreht und beim zurückgehen alles bis } löscht. das währe dan für jeden komentar block einzeln

ps. zeile 7 und 8 sind kein versehen. darum geht es ja gerade ;-)


maxk - Di 26.07.05 00:21

Ist jetzt wahrscheinlich nicht so sauber geschrieben, aber ich schlaf schon fast ;)

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
function RemoveComments(Str:string):string;
var p1,p2:integer;
begin
 Result:='';
 while length(Str)>0 do begin
  p1:=pos('{',Str);

  if p1>0 then begin
   Result:=Result+copy(Str,1,p1-1);
   Delete(Str,1,p1-1);
  end else begin
   Result:=Result+Str;
   Str:='';
  end;

  p2:=pos('}',Str);
  if p2>0 then Delete(Str,1,p2);
 end;
end;


Gruß,
maxk


sahib - Di 26.07.05 01:30

Hi.


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
function EntferneKommentare(s: String): String;
var
  KlammerZaehler: Word;
  i             : LongWord;
begin
  KlammerZaehler := 0;
  for i := Length(s) downto 1 do begin
    if s[i] = '}' then
      inc(KlammerZaehler)
    else
    if s[i] = '{' then
      dec(KlammerZaehler);

    if (KlammerZaehler > 0or (s[i] = '{'then
      Delete(s, i, 1)
  end;
  Result := s
end;


*EDIT*: Result-Zeile vergessen...

Viele Grüße,
Christian


BenBE - Di 26.07.05 01:34

Das Löschen der Kommenare über einzelzeichen würde ich so nicht realisieren (jedes Zeichen einzeln aus einem Kommentar löschen), da bei jedem Löschvorgang ein komplett neuer String angelegt wird, was besonders bei langen Eingabe-Strings zu extremen Problemen führen kann.


maxk - Di 26.07.05 01:36

Verblüffent einfach :lol: Allerdings könnte ich mir vorstellen, dass das länger dauert, als mit Copy zu arbeiten, da du ja wirklich jedes Zeichen einzeln durchgehen musst. Da ich aber nur eine PE Version habe, weiss ich auch nicht wirklich, wie Copy&Delete intern arbeiten :(

Problem übrigens:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
{
1
{
2
}

1
}
Was davon gehighlighted ist, sieht Delphi selber nichtmehr als Kommentar.

Gruß,
maxk


BenBE - Di 26.07.05 01:42

Jup. Zum einen Das, zum anderen folgender Source:

Str := '{Hallo}'; würden bisher beide Sources als Kommentar sehen und daher löschen.

Für das Kommentar-Handling von Delphi ist die erste Version richtig, für verschachtelte die zweite, für syntaktisch korrektes Entfernen der Kommentare in meinem Beispiel-Source keiner von beiden.


sahib - Di 26.07.05 01:43

Mit der Kopie des Strings stimmt natürlich. Allerdings würde ich eine solche Datei eh in einen MemoryStream laden und dort die Daten manipulieren.

Ein zu Bett gehender Christian


maxk - Di 26.07.05 01:57

So, damit sollte alles berücksichtig sein:

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:
function RemoveComment(Str:string):string;
const SD = '''';
var pS,pC:integer;
begin
 Result:='';
 while length(Str)>0 do begin
  pC:=pos('{',Str);
  if pC=0 then begin
   // Nothing to do
   Result:=Result+Str;
   Str:='';
  end else begin
   pS:=pos(SD,Str);
   if (pS<pC) and (pS<>0then begin
    // String
    Result:=Result+copy(Str,1,pS);
    Delete(Str,1,pS);
    pS:=pos(SD,Str);
    if pS>0 then begin
     Result:=Result+copy(Str,1,pS);
     Delete(Str,1,pS);
    end else begin
     raise Exception.Create('Unterminated string');
    end;
   end else begin
    // Comment
    Result:=Result+copy(Str,1,pC-1);
    Delete(Str,1,pC);
    pC:=pos('}',Str);
    if pC>0 then begin
     Delete(Str,1,pC);
    end else begin
     raise Exception.Create('Unexpected end of content in comment');
    end;
   end;
  end;
 end;
end;


Gruß,
maxk


sahib - Di 26.07.05 13:50

So, in der Mittagspause habe ich auch noch ein wenig gefummelt ;)


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:
function EntferneKommentar(s: String): String;
var
  cnt, i, j       : LongWord;
  NichtImKommentar: Boolean;
begin
  if s = '' then Exit;
  i := 1; cnt := 0;
  NichtImKommentar := True;
  while i < Length(s) do begin
    if NichtImKommentar then
      NichtImKommentar := s[i] <> #39  // Hochkomma
    else
    if not NichtImKommentar then
      NichtImKommentar := s[i] = #39;

    if NichtImKommentar and (s[i] = '{'then begin
      j := i; inc(cnt);
      while (j < Length(s)) and (cnt > 0do begin
        inc(j);
        if s[j] = '{' then inc(cnt) else
        if s[j] = '}' then dec(cnt)
      end;
      if s[j] <> '}' then
        raise Exception.Create('Abschliessende Klammer fehlt.');
      Delete(s, i, j - i + 1)
    end;
    inc(i)
  end;
  Result := s
end;


Ich habe gerade mal für beide Funktionen die Zeit gemessen (gleicher Datensatz: 6.2MB):
EntferneKommentar: 538 ms (der Speicherverbrauch ist auch deutlich geringer)
RemoveComment : 21860 ms

Und wenn man das dann noch über einen MemoryStream macht und nur die Pointer bewegt, dürfte das noch etwas schneller werden?

Das macht ja richtig spaß ;) Und Benny, Deine Meinung als Profi ist mir natürlich auch wichtig, wegen Stil, Anwendbarkeit etc. Du siehst das doch immer mit anderen Augen. Ich ziele immer auf Spezialfälle ab, und mache die schnell und mit wenig Speichernutzung.

Viele Grüße,
Christian


maxk - Di 26.07.05 14:30

user profile iconsahib hat folgendes geschrieben:
Ich habe gerade mal für beide Funktionen die Zeit gemessen (gleicher Datensatz: 6.2MB):
EntferneKommentar: 538 ms (der Speicherverbrauch ist auch deutlich geringer)
RemoveComment : 21860 ms
Angeber :tongue:

Dass der Speicherverbrauch bei deiner Methode etwa halb so groß ist, ist logisch. Aber dass sie auch schneller ist, hätte ich nicht gedacht (glaub ich dir aber). Wobei mich interessieren würde, warum das so ist? Wie arbeiten pos, copy und delete den intern? Trotzdem Respekt!

Gruß,
maxk


jaenicke - Di 26.07.05 15:18

user profile iconmaxk hat folgendes geschrieben:
user profile iconsahib hat folgendes geschrieben:
Ich habe gerade mal für beide Funktionen die Zeit gemessen (gleicher Datensatz: 6.2MB):
EntferneKommentar: 538 ms (der Speicherverbrauch ist auch deutlich geringer)
RemoveComment : 21860 ms
Angeber :tongue:

Dass der Speicherverbrauch bei deiner Methode etwa halb so groß ist, ist logisch. Aber dass sie auch schneller ist, hätte ich nicht gedacht (glaub ich dir aber). Wobei mich interessieren würde, warum das so ist? Wie arbeiten pos, copy und delete den intern?

Das liegt unter anderem an Pos, das geht ja auch zeichenweise durch (statt den schnelleren Boyer-Moore-Algorithmus zu benutzen).
Damit konnte ich bei einer Umsetzung in Assembler bei größeren Pattern das Pos nochmal deutlich schneller machen.

Mit BM werden die Zeichen abhängig vom gesuchten Text übersprungen, die mit dem letzten Vergleich sowieso nicht in Frage kommen. (Kann ich auch gerne noch genauer erklären :wink: )


sahib - Di 26.07.05 19:09

Hallo Sebastian.

user profile iconjaenicke hat folgendes geschrieben:
Das liegt unter anderem an Pos, das geht ja auch zeichenweise durch (statt den schnelleren Boyer-Moore-Algorithmus zu benutzen).
Damit konnte ich bei einer Umsetzung in Assembler bei größeren Pattern das Pos nochmal deutlich schneller machen.


Boyer-Moore (BM) ist in diesem speziellen Falle allerdings völlig unangebracht. Wie Dir aufgefallen ist, hat die zu suchende Zeichenkette die Länge eins, nämlich '{'. Und da ist BM langsamer. Ohne Zweifel ist er natürlich überlegen, wenn die Suchworte länger sind.

Warum mein Code schneller ist? Zum einen habe ich nur einen Prozeduraufruf (Delete) und zum anderen kopiere ich nichts hin und her (ok, delete dürfte das machen). Aber selbst diese aufgerufene Prozedur könnte noch durch einen einfachen Move-Befehl ersetzt werden können. Kurz vor der Ergebnisübergabe müsste das nur noch die neue Zeichenkettenlänge mit SetLength neu gesetzt werden.

Christian


jaenicke - Mi 27.07.05 16:47

Ok, du hast recht, in diesem Fall bringt Boyer Moore nix, bzw. ist durch die kompliziertere Umsetzung sogar deutlich langsamer. (Wohl auch der Grund, der Borland dazu veranlasst hat, die Standard-Methode zu benutzen)

Was ich damit ausdrücken wollte war auch nur, dass die internen Routinen von Borland nicht unbedingt auf absolute Geschwindigkeit optimiert sind, bzw. das auch nicht sein können, weil sie ja das restliche Programm nicht kennen.