Autor Beitrag
bockwurst
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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
ausblenden Delphi-Quelltext
1:
procedure PDelete(var S: PChar; Index, Count:Integer);					


und aus
ausblenden Delphi-Quelltext
1:
procedure Insert(Source: string; var S: string; Index: Integer);					

soll
ausblenden Delphi-Quelltext
1:
procedure PInsert(Source: string; var S: PChar; Index: Integer);					

werden.

Wie geht dies am Besten?
Lg. Andreas

Moderiert von user profile iconAXMD: Delphi-Tags hinzugefügt.
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: Di 15.03.05 11:31 
ich habe es mal so versucht (erstmals nur für Delete)

ausblenden volle Höhe 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:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Di 15.03.05 11:53 
Ich hatte an sowas gedacht:

ausblenden volle Höhe 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:
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 // Das b und das c löschen
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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,

ausblenden volle Höhe 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:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Di 15.03.05 13:46 
Meine Version für Insert:

ausblenden volle Höhe 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:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
{
// Version für WIN32:
function PInsert(quell: String; var sMPL: PChar; pos: integer): boolean;
var
  Pre, Post: String;
begin
  Result := False;
  Pre := Copy(sMpl,1,pos);
  Post := Copy(sMpl,pos+1,9999);
  StrDispose(sMPL);
  sMPL := StrNew(PChar(Pre + quell + Post));
end;
}


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); // Plus eins für die 0
  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 // Das b und das c löschen
    ShowMEssage('Fehler')
  else
    ShowMEssage(sMPL);

  if PInsert('bc',sMPL,1) then // Das b und das c einfügen
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: 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. :wink:

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: Do 17.03.05 13:23 
PDelete hat leider wohl auch Probleme mit der Speicherverwaltung
bockwurst Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: Do 17.03.05 14:12 
Ich habe jeweils wdbee's Lösungen genommen
mit folgenden Prog bekomme ich wenigstens eine Exception

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 17.03.05 15:03 
Zeiger auf konstante Texte dürfen so nicht verwendet werden, die Strings müssen auf dem Heap liegen!

ausblenden volle Höhe 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:
procedure TForm1.Button1Click(Sender: TObject);
var
  sMPL: PChar;
  pos, lang: integer;
  i: integer;
begin
  sMPL := nil; // Als unbenutzt markieren

  pos := 4;    // 5. Buchstabe
  lang := 6;   // 6 Zeichen lang

  // Zu Beginn der Schleife ist sMPL = nil!
  for i := 0 to 10000 do                          // Stresstest
  begin
    if i mod 2 = 0 then                           // Hier muss ein neuer
      sMPL := StrNew('Ein Praxisnahes Beipiel')   // String erzeugt werden
    else                                          // sMPL darf kein Zeiger
      sMPL := StrNew('delphi hat euch alle lieb');// auf eine Konstante sein!

    PDelete(sMPL, pos, lang);                     // Löschen

    if i mod 2 = 0 then                           // und wieder Einfügen
      PInsert('Praxis', sMPL, pos)
    else
      PInsert('hi hat', sMPL, pos);

    // Speicher wieder freigeben                  // Falls bei der Fallunter-
    if sMPL <> nil then                           // scheidung was übersehen
      StrDispose(sMPL);                           // wurde auf not nil testen
    // damit er oben wieder neu zugeordnet werden kann

    // Zum Ende der Schleife ist sMPL undefiniert
    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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: Do 17.03.05 15:38 
Danke Zeigergott!

Ich dachte immer

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var p:cPChar
begin
  p:=StrAlloc(20);
  StrPCopy(p,'XYZ');
  ....
  StrDispose(p);


ist das selbe wie

ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Do 17.03.05 15:51 
Nun ja:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
  p := StrAlloc(20);  // 20 Zeichen Platz auf dem Heap
  StrPCopy(p,'XYZ');  // Nur drei Zeichen kopieren
  ...                 // Da musst du dann aufpassen ob 0 (Stringende) mitgezählt ist
  StrDispos(p);       // Platz auf dem Heap freigeben

ist schon sehr ähnlich zu
ausblenden Delphi-Quelltext
1:
2:
3:
  p := StrNew('XYZ');  // Platz passt genau und ist auf dem Heap
  ...                  // Nix denken müssen
  StrDispos(p);        // Platz auf dem Heap freigeben

Aber was halt garnicht passt, ist
ausblenden Delphi-Quelltext
1:
2:
3:
  p := 'XYZ';          // Der Platz its HIER und nicht auf dem Heap
  ...                  // Nix gedacht haben
  StrDispos(p);        // Platz auf dem Heap freigeben
bockwurst Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: 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

ausblenden 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');    // init
 
  // jetzt aufrufen einer Funtion die entweder wieder einer Funktionen
  // den Zeiger p Übergibt oder direkt PInsert / PDelete aufruft
 
  Funktion_PDelete_aufrufen(p:PChar; ....) 
  // diese Funktion ruft PDelete auf
  // und noch PInsert
  // danch habe ich Speicher Probleme
  // muß ich evtl. die Funktion Funktion_PDelete_aufrufen anders implemntieren
  //. z.B.
  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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: 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.
ausblenden 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); // Falsch
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!');
//  TuWas(P);
  TuWasAberRichtig(P);
  ShowMessage(P); // Was wir hier angezeigt?
  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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 191

win98 /XP
D1 D5 Prof DE2005PE
BeitragVerfasst: Fr 18.03.05 14:41 
Bei dir in PInsert
dur hast da stehen

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
 Function PInsert(...
  ...
 Q := StrAlloc(N+Len);
 // muß es nicht heissen
 Q := StrAlloc(N+Len)+1;


Andreas
wdbee
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 628
Erhaltene Danke: 1



BeitragVerfasst: Fr 18.03.05 14:57 
Fast: Du hast Recht mit deiner Kritik! :oops:
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.