| Autor |
Beitrag |
bockwurst
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Di 15.03.05 10:22
Hallo zusammen,
kann mir jemannd sagen, wie ich ohne ein Array[1..N] of Char die Prozeduren Delete und Insert ganz genauso mit nullterminierten Strings anwenden kann?
aus
procedure Delete(var S: string; Index, Count:Integer);
soll
Delphi-Quelltext 1:
| procedure PDelete(var S: PChar; Index, Count:Integer); |
und aus
Delphi-Quelltext 1:
| procedure Insert(Source: string; var S: string; Index: Integer); |
soll
Delphi-Quelltext 1:
| procedure PInsert(Source: string; var S: PChar; Index: Integer); |
werden.
Wie geht dies am Besten?
Lg. Andreas
Moderiert von AXMD: Delphi-Tags hinzugefügt.
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Di 15.03.05 10:57
Das ist nicht ganz trivial. Der springende Punkt ist, das die Speichervwerwaltung das mitbekommen muss, sonst bleiben evt. Speicherlecks übrig. Bei PDelete wäre das eher unkritisch, wenn bei der Freigabe die alte Länge wirksam bleibt und nicht die erste 0 ausgewertet wird bzw. eine Längenangabe die alte Länge des Speicherbereichs für die Speicherverwaltug bereit hält.
Bei PInsert ist das schwieriger, da du Platz für einen längeren String brauchst. Den kannst du in der Regel nur an einem anderen Platz im Speicher beschaffen, musst also den Zeiger auf den Speicher austauschen. Nun ist es im Einzelfall zu prüfen, ob dann die Speicherverwaltung noch funktioniert.
Es gibt da ja zwei Fälle die du dabei auseinanderhalten musst. Wenn du den Speicher dynamisch beschafft hast, aber nur den einen Zeiger verwendest, könntest du in der PInsert-Funktion neuen Speicher beschaffen und den alten gleich freigeben. Wenn du aber zwei Zeiger auf den selben Speicher verwendest, dann wäre eine der beiden dann ungültig!
Da das nicht trivial ist, bietet Delphi ja die String-Objekte, die das alles kapseln.
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Di 15.03.05 11:05
hallo wdbee,
ich benutze ,leider, hier für dies Delphi 1. Wie kann ich den da auf Delphi zurück greifen?
Mit dem Speicher bekomme ich wohl keine Problemme. Der String wird einfach irgendwo, in einen neuen Kopiert und danach ganz freigegeb und dann wieder ganz neu reserviert. Dies sollte wohl möglichsein. Zu sagen ist da vielleicht noch, daß ich nicht auf PascalString zurückgreifen kann, da der PChar String länger als 255 Zeichen sein kann/sollte.
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Di 15.03.05 11:14
Also wenn du dich versicherst, dass das mit dem Speicher so klappt, dann nimm in den Funktionen Zeiger (PChar).
Bei PDelete: Zielzeiger und Quellzeiger auf deinen Startzeiger setzen. Zielzeiger um Startindex inkrementieren, Quellzeiger um Startindex+Musterlänge inkrementieren. Dann in einer Schleife Musterlänge Chars umkopieren und die beiden Zeiger immer um 1 inkrementieren. Als letztes noch die 0 an das neue Ende kopieren.
PInsert geht dann analog, aber mit Beschaffung und Freigabe des Speichers.
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Di 15.03.05 11:31
ich habe es mal so versucht (erstmals nur für Delete)
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:
| Function PDelete(var sMPL:PChar; pos,lang:integer):boolean; var pS1, pS2:PChar; begin try result:=true; if (sMPL=nil) or (pos<0) or (lang<1) or (pos>StrLen(sMPL)) then exit; try pS1:=nil; pS2:=nil;
pS1:=StrAlloc(StrLen(sMPL)+1); pS2:=StrAlloc(StrLen(sMPL)+1);
pS1:=''; pS2:=''; if pos>1 then pS1:=StrLCopy(pS1,sMPL,pos-1); pS2:=@sMPL[pos+lang-1];
StrCat(pS1,pS2); sMPL:=''; StrCopy(sMPL, pS1);
finally StrDispose(pS1); StrDispose(pS1); end;
except result:=false; end; end; |
sagt mir bitte, wenn ich etwas falsch gemacht habe, oder es besser bzw, einfacher geht.
Es wäre aber auch schon zu hören, wenn man es so machen könnte.
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Di 15.03.05 11:53
Ich hatte an sowas gedacht:
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:
| function PDelete(var sMPL: PChar; pos,lang: integer): boolean; var I: Integer; pS1, pS2: PChar; Len: Integer; begin Result := true; Len := StrLen(sMPL);
if (sMPL = nil) or (pos < 0) or (lang < 1) or (pos > Len - Lang) then Exit;
pS1 := sMPL; Inc(pS1,pos); pS2 := sMPL; Inc(pS2,pos+lang);
for I := 0 to Len - Lang - 2 do begin pS1^ := pS2^; Inc(pS1); Inc(pS2); end;
pS1^ := Chr(0); Result := false; end;
procedure TForm1.Button1Click(Sender: TObject); var sMPL: PChar; begin sMPL := StrNew('AbcDE');
if PDelete(sMPL,1,2) then ShowMEssage('Fehler') else ShowMEssage(sMPL);
StrDispose(sMPL); end; |
Der Index beginnt so mit 0 für das erste Zeichen!
// Edit: Die Überprüfung der Länge noch ergänzt und Stringende explizit gesetzt
// Edit2: Schleife um 1 kürzer wegen explizit geseztem 0-Zeichen!
Zuletzt bearbeitet von wdbee am Mi 23.03.05 11:56, insgesamt 1-mal bearbeitet
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Di 15.03.05 13:07
Hallo wdbee,
deine Lösung sieht einfacher aus. Ich teste sie mal
Habe jetzt auch für insert erledigt,
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:
| Function PInsert(quell:String; var sMPL:PChar; pos:integer):boolean; var pS1, pS2, PS3:PChar; begin try result:=true; try pS1:=nil; pS2:=nil; pS3:=nil;
pS1:=StrAlloc( StrLen(sMPL) + Length(quell) +1 ); pS2:=StrAlloc( StrLen(sMPL) + Length(quell) +1 ); pS3:=StrAlloc( StrLen(sMPL) + Length(quell) +1 );
if pos>1 then pS1:=StrLCopy(pS1,sMPL,pos-1) else pS1:='';
pS2:=@sMPL[pos-1]; StrPCopy(pS3, quell);
StrCat(pS3, pS2); StrCat(pS1, pS3);
StrDispose(sMPL); sMPL:=nil; sMPL:=StrAlloc( StrLen(pS1) +1 ); sMPL:=''; StrCopy(sMPL, pS1);
finally StrDispose(pS1); StrDispose(pS2); StrDisPose(pS3); end;
except result:=false; end; end; |
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Di 15.03.05 13:46
Meine Version für Insert:
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: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72:
|
function PInsert(quell: String; var sMPL: PChar; pos: integer): boolean; var Pre, P, Q: PChar; I, Len, N: Integer; begin Result := True; Pre := sMpl; N := StrLen(Pre); if pos >= N then Exit;
Len := Length(Quell); Q := StrAlloc(N+Len+1); P := Q; for I := 0 to pos - 1 do begin Q^ := Pre^; Inc(Q); Inc(Pre); end; for I := 1 to Len do begin Q^ := Quell[I]; Inc(Q); end; for I := Pos to N do begin Q^ := Pre^; Inc(Q); Inc(Pre); end; Q^ := Chr(0);
StrDispose(sMPL); sMPL := P;
Result := False; end;
procedure TForm1.Button1Click(Sender: TObject); var sMPL: PChar; begin sMPL := StrNew('AbcDE');
if PDelete(sMPL,1,2) then ShowMEssage('Fehler') else ShowMEssage(sMPL);
if PInsert('bc',sMPL,1) then ShowMEssage('Fehler') else ShowMEssage(sMPL);
StrDispose(sMPL); end; |
// Edit: Post wird nicht gebraucht
// Edit2: Platz für die 0
Zuletzt bearbeitet von wdbee am Fr 18.03.05 15:05, insgesamt 2-mal bearbeitet
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Di 15.03.05 14:35
Danke Wdbee,
ich werde alles ganz genaus noch testen. Aber warum ist dies denn nicht in Delphi enthalten? (Jedenfalls für Delphi 1)
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Di 15.03.05 14:42
Teste vor allem mal das Verhalten an den Rändern. Wenn man aus 5 Zeichen an Position 5 zwei Zeichen löschen will, kann ein Fehler kommen oder einfach nur ein Zeichen entfernt werden. Realisier da dein eigenes Konzept.
Delphi hatte nicht solchen Erfolg weil alles drin war, sondern weil die Auswahl und die Umsetzung dessen was drin war, genial war. Alles so genial umzusetzten hätte einfach zulange gedauert (Time to market).
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Do 17.03.05 13:00
Hallo wdbee,
so richtig ist das mit dem Insert jedenfalls nicht. In meinem Prog kommen immer später irgendwann Allgeime-Fehlermeldungen (bei anderen "normalen" Functionen), wenn ich diese Function PInsert aufrufe. Ich habe deine und meine genommen, immer das selbe. In der Function selbst kommt es zu keiner Exception. Mit Zeigern ist es schon nicht einfach. Ich werde es jetzt nocht mehr testen müssen/dürfen.
Es lebe der Pascal-String !!!!!
Andreas
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Do 17.03.05 13:23
PDelete hat leider wohl auch Probleme mit der Speicherverwaltung
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Do 17.03.05 14:12
Ich habe jeweils wdbee's Lösungen genommen
mit folgenden Prog bekomme ich wenigstens eine Exception
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:
| procedure TForm1.Button1Click(Sender: TObject); var sMPL:PChar; pos, lang:integer; i:integer; begin sMPL:=nil; sMPL:=StrAlloc(300);
pos:=4; lang:=6;
for i:=0 to 3 do begin if i mod 2=0 then sMPL:='Ein Praxisnahes Beipiel' else sMPL:='delphi hat euch alle lieb';
PDelete(sMPL, pos,lang); if i mod 2=0 then PInsert('Praxis', sMPL, pos) else PInsert('hi hat', sMPL, pos); end;
StrDispose(sMPL);
end; |
Die Exception kommt in PInsert/StrDispose(sMPL);
Wie kann ich es richtig machen?
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Do 17.03.05 15:03
Zeiger auf konstante Texte dürfen so nicht verwendet werden, die Strings müssen auf dem Heap liegen!
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:
| procedure TForm1.Button1Click(Sender: TObject); var sMPL: PChar; pos, lang: integer; i: integer; begin sMPL := nil; pos := 4; lang := 6; for i := 0 to 10000 do begin if i mod 2 = 0 then sMPL := StrNew('Ein Praxisnahes Beipiel') else sMPL := StrNew('delphi hat euch alle lieb'); PDelete(sMPL, pos, lang); if i mod 2 = 0 then PInsert('Praxis', sMPL, pos) else PInsert('hi hat', sMPL, pos);
if sMPL <> nil then StrDispose(sMPL); sMPL := nil; end; end; |
Der Unterschied ist sMPL := StrNew('Ein Praxisnahes Beipiel') beschafft neuen Speicher und kopiert den konstanten Text dorthin. Bei dir wird der Zeiger sMPL:='Ein Praxisnahes Beipiel' auf den konstanten Text gesetzt. Dabei geht der Speicherblock verloren, den du vorher mit StrAlloc bereitgestellt hattest. Deshalb kannst du ihn auch nicht mehr mit StrDispose freigeben, denn der übergebene Zeiger zeigt nicht mehr auf den Speicherblock. Du hast also ein Speicherleck produziert.
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Do 17.03.05 15:38
Danke Zeigergott!
Ich dachte immer
Delphi-Quelltext 1: 2: 3: 4: 5: 6:
| var p:cPChar begin p:=StrAlloc(20); StrPCopy(p,'XYZ'); .... StrDispose(p); |
ist das selbe wie
Delphi-Quelltext 1: 2: 3: 4: 5:
| var p:PChar; begin StrNew('XYZ'); .... StrDispose(p); |
ich werde meine Prog leicht "StrNew"ern müssen.
Vielen Dank nochmals. Wenn das endlich klappt ohhh, wie wäre das ein schönes Wochenende
lg. Andreas
(Ich werde dir Meldung geben)
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Do 17.03.05 15:51
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Fr 18.03.05 14:08
Hallo wdbee,
ich habe immer noch ein Problem mit PDelete & PInsert. Immer wenn ich meine Variable sMPL (PChar) durch mehre Functionen übergebe und dann ändere, bekomme ich Probleme mit meinen Zeiger / Speicher folgendes Beispiel
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| Procedure .... var p:PChar begin p:=StrNew('wdnee ist ein Zeigergott'); Funktion_PDelete_aufrufen(p:PChar; ....) Funktion_PDelete_aufrufen(VAR p:PChar; ....) ... StrDispose(p) end; |
Oder muß ich in Funktion_PDelete_aufrufen(p:PChar; ....) jedesmal StrNew auf den Heap legen. Mit einer Globalen Variablen wurde ich glaube ich keine Probleme haben, aber da ist ja nicht menschlich, oder doch...?
Gruß Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Fr 18.03.05 14:34
Das ist doch klar. Wenn du die TuWas Funktion benutzt, änderst du das Ziel des Zeigers.
Damit die Änderung außerhalb ankommt, musst du also nicht den Zeiger selbst sondern dessen Adresse übergeben, also den Zeiger auf den Zeiger. Das macht man wie du schon vermutet hast mit var.
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:
| procedure TForm1.TuWas(P: PChar); begin ShowMessage(P); StrDispose(P); P := StrNew('WDBEE weiss das!'); ShowMessage(P); end;
procedure TForm1.TuWasAberRichtig(var P: PChar); begin ShowMessage(P); StrDispose(P); P := StrNew('WDBEE weiss das!'); ShowMessage(P); end;
procedure TForm1.Button1Click(Sender: TObject); var P: PChar; begin P := StrNew('WDBEE hat alle diese Fehler auch mal gemacht!'); TuWasAberRichtig(P); ShowMessage(P); StrDispose(P); end; |
Wenn du das hier mal ausführst, siehst du, dass kein Fehler auftritt. Wenn du aber TuWas aufrufst, dann kommt dein Fehler. (Ganz ohne unsere P-Funktionen).
|
|
bockwurst 
      
Beiträge: 191
win98 /XP
D1 D5 Prof DE2005PE
|
Verfasst: Fr 18.03.05 14:41
Bei dir in PInsert
dur hast da stehen
Delphi-Quelltext 1: 2: 3: 4: 5:
| Function PInsert(... ... Q := StrAlloc(N+Len); Q := StrAlloc(N+Len)+1; |
Andreas
|
|
wdbee
      
Beiträge: 628
Erhaltene Danke: 1
|
Verfasst: Fr 18.03.05 14:57
Fast: Du hast Recht mit deiner Kritik!
Aber das +1 muss in die Klammer! Ich habs oben mal verbessert.
// Edit
Aber da ist noch ein Fehler: Die erste Schleife läuft von 0 bis Pos-1, dann kommen die Len neuen Bytes, aber als letztes dürfen dann nur noch die von Pos bis N folgen!
Gibt dann die Länge Len+N+1.
|
|