Entwickler-Ecke

Dateizugriff - Strings zwischen Simikolons lesen


ebs - So 20.03.05 18:04
Titel: Strings zwischen Simikolons lesen
Hallo,
ich bin absoluter Anfänger und habe ein Problem mit Dateien. Ich greife mit Delphi auf Textdateien zu, in denen sowohl Strings in Zeilen, als auch zwischen Simikolons stehen. Mein Problem ist, dass Delphi nun immer nur die Spalten als ein String sieht, und niemals die Simikolons. Gibt es irgendeine möglichkeit, dass Delphi auch die Strings zwischen den Simikolons erkennt, ohne dass ich die Datei neu umformatieren muss? Wenn es sein muss, kann ich auch die Simikolons in andere Zeichen ändern.

Hier ein Beispiel:

Quelltext
1:
2:
3:
i13;15;25;58;256;128;16;32
l15;564;67;31;46;54;
k88;546;54;5



Und der Quelltext:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
procedure TForm1.Open1Click(Sender: TObject);
var
  F: TextFile;
  S: string;
begin
begin
  if OpenDialog1.Execute then          { Dialog zum Dateiöffnen anzeigen }
  begin
    AssignFile(F, OpenDialog1.FileName);   { Datei ausgewählt }
    Reset(F);
    Readln(F, S);                          { Erste Zeile der Datei lesen }
    Edit1.Text := S;                       { String in ein TEdit schreiben }
    CloseFile(F);

end;
end;
end;

Ich danke im voraus!

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


AXMD - So 20.03.05 18:07

:welcome:

Suche in: Delphi-Forum, Delphi-Library POS und Suche in: Delphi-Forum, Delphi-Library COPY helfen da ;)

AXMD


F34r0fTh3D4rk - So 20.03.05 18:46


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
Function GetWordOfAnsiString(Text : string; WordPos : Word): string;
var
 p  : PChar;
begin
  p  := PChar(Text);
  repeat
    Dec(WordPos);
    while p^ <> #0 do
    begin
      if WordPos = 0 then
       Result := Result + p^;
      Inc(p);
    end;
    if WordPos = 0 then Exit;
    Inc(p);
  until p^ = #0;
end;

hilft da auch ^^

#0 lässt sich durch jedes zeichen ersetzen


wulfskin - So 20.03.05 18:53

Suche in: Delphi-Forum, Delphi-Library EXPLODE


Raphael O. - So 20.03.05 18:58

@wulfskin: darauf wollte ich auch hinweisen, nur dass ich den Link noch gesucht hab ^^
damit die Mühe nicht vergebens war hier der Link ;)
http://www.delphi-forum.de/viewtopic.php?t=13017


delfiphan - So 20.03.05 19:01

Die zwei Funktionen explode und implode müssen einfach mal gepostet werden...

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:
procedure explode(S: String; C: Char; List: TStrings);
Var
 I, J: Integer;
begin
 List.Clear;
 if S = '' then
  exit;
 J := 1;
 for I := 1 to length(S) do
  if S[I] = C then
  begin
   List.Add(Copy(S, J, I-J));
   J := I+1;
  end;
 List.Add(Copy(S, J, I-J));
end;

function implode(List: TStrings; C: Char): String;
Var
 I: Integer;
begin
 if List.Count = 0 then
 begin
  Result := '';
  exit;
 end else
  Result := List.Strings[0];
 For I := 1 to List.Count-1 do
  Result := Result + C + List.Strings[I];
end;

Anwendung:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
Var
 List : TStringList;
 S : String;
 I : Integer;
begin
 S := 'Eintrag1;Eintrag2;Eintrag3';

 List := TStringList.Create;

 Explode(S,';', List); // String->List
 for I := 0 to List.Count-1 do
  // in List.Strings[I] sind die Einträge

 S := Implode(List, ';'); // List->String
 List.Free;
end;

Oops, da hat einer schon nen Link gepostet. Meine Funktionen also als Alternative :)


F34r0fTh3D4rk - So 20.03.05 19:05

oder meine variante, die ich (pers) einfacher finde :roll:


delfiphan - So 20.03.05 19:08

Naja, die Prozedur ExtractStrings (unit Classes) ist mir erst heute aufgefallen, damit geht's natürlich auch.

@F34r0fTh3D4rk: Deine Prozedur macht irgendwas, auf jeden Fall nicht das gesuchte. Er/Sie will einen String, welches das Zeichen ';' enthält in mehrere Strings unterteilen.


hansa - So 20.03.05 22:21

Wo kommen denn die Semikolons überhaupt her ?


delfiphan - So 20.03.05 22:28

Dateien im CVS Format sind so. Wenn du z.B. ein Excel Sheet als CVS abspeicherst.


hansa - So 20.03.05 22:37

Das halte ich eher für ein Gerücht. 8) Zumindest im deutschsprachigen Raum verwendet man kein CSV. Wenn das dein CVS überhaupt ist. Auch nicht in Excel. Wozu so was verwenden ?


delfiphan - So 20.03.05 22:46

CSV, sorry, Tippfehler. Dass man mit Excel CSV Dateien erstellen kann ist doch kein Gerücht!? CSV ist praktisch und einfach zu implementieren. Oder erstellt deine Delphiapplikation eine xls Datei? Im Übrigen haben nicht alle Excel.


hansa - So 20.03.05 23:00

Wir reden hoffentlich von "comma separated values". Und die sind Schrott. :mrgreen: Das sollte heutzutage nicht mehr verwendet werden. Egal wie das Trennzeichen sich schimpft. Ich kann doch nicht ein Zeichen als Trennzeichen verwenden, sofern auch nur eine sehr geringe Wahrscheinlichkeit besteht, daß es innerhalb der Daten selbst verwendet wird. :shock:


delfiphan - So 20.03.05 23:05

Stimmt schon, aber für Zahlen kann man die schon gebrauchen...


Lannes - So 20.03.05 23:22

Hallo,
user profile iconhansa hat folgendes geschrieben:
Das halte ich eher für ein Gerücht. 8) Zumindest im deutschsprachigen Raum verwendet man kein CSV. Wenn das dein CVS überhaupt ist. Auch nicht in Excel. Wozu so was verwenden ?

Zitat
Ab 26. September 1999 sind alle Rinder in Deutschland gemäß
§ 24f der Viehverkehrsverordnung zu erfassen.
:arrow: Hi-Tier Bundesweite Tierdatenbank [http://www.hi-tier.de/Entwicklung/Konzept/HITP/csvformat.html]
:arrow: Import Exceltabellen in die Wikipedia [http://de.wikipedia.org/wiki/Benutzer:Duesentrieb/csv2wp]
:arrow: Arcor-Abrechnungen mit Webbill [http://keyboardsamurais.de/arcor_webbill_tool.html]

user profile iconhansa hat folgendes geschrieben:
Wir reden hoffentlich von "comma separated values". Und die sind Schrott. :mrgreen: Das sollte heutzutage nicht mehr verwendet werden. Egal wie das Trennzeichen sich schimpft. Ich kann doch nicht ein Zeichen als Trennzeichen verwenden, sofern auch nur eine sehr geringe Wahrscheinlichkeit besteht, daß es innerhalb der Daten selbst verwendet wird. :shock:

Doch:

Quelltext
1:
1;"Text mit "" im Text";"oder mit ; im Feld";123                    

das dann aufgeteilt(mit einem guten Programm :? ):

Quelltext
1:
2:
3:
4:
1
Text mit " im Text
oder mit ; im Feld
123


MfG Lannes


hansa - Mo 21.03.05 02:42

Tja, die Links gucke ich mir erst gar nicht an. :lol: Ich lese da was von Rindvieh-Züchtung. Davon habe ich leider keine Ahnung und überlasse dieses Thema demjenigen, der weiß, wovon er spricht. 8) Ansonsten empfehle ich eben, feste Feldlängen zu verwenden. Nachzulesen im "Entwickler".

Alternative wäre natürlich auch, eine Textdatei solange umzubauen, bis sie irgendwie paßt. :P Ja, dann mal weiter so.


ebs - Mo 21.03.05 09:07
Titel: Danke!
Hallo,
ihr habt mir sehr geholfen. Eure Methoden funktionieren genauso, wie ich das haben wollte. Vielen Dank!


opfer.der.genauigkeit - Mo 21.03.05 09:25

Hi ebs,

sicherlich nicht die performanteste aber "faulste" Methode wäre das hier :lol: :


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
var
  List: TStringList;
  s   : string;
  i   : integer;
begin
  List := TStringList.Create;

  s := 'test1;test2;test3;test4';
  List.Delimiter := ';';
  List.CommaText := s;

  for i := 0 to ( List.Count - 1 ) do begin
    // was auch immer
  end;

  List.Free;
end;


F34r0fTh3D4rk - Mo 21.03.05 11:52

@delphiphan: genau dieses "irgendwas", ist das gesuchte !

man kann damit einen string, der durch trennzeichen getrennt ist, in mehere einzelne strings aufteilen, probiers aus !


delfiphan - Mo 21.03.05 13:01

Müsstest du aber noch ein Beispiel mitliefern; oder zu mindest sagen, was WordPos ist.
Man will 1 String in mehrere Strings zerteilen. Dabei soll man auch angeben können, welches Zeichen das Trennzeichen ist. Bei deiner Prozedur muss man irgendwie die Wortposition kennen, und man bekommt jeweils nur ein String zurück :?: Muss man die Prozedur für jeden Eintrag ausführen, und sich jeweils die Position merken? Muss man selbst prüfen, ob man am Ende des Strings ist? Wo geb ich das Trennzeichen an, muss ich da jedes mal den Source an mehreren Stellen ändern gehn? Was ist dann der Vorteil deiner Prozedur gegenüber Pos und Copy? ....


SchelmVomElm - Mo 21.03.05 13:07

Also wie das gehen soll ist mir auch nicht klar! Wenn WordPos in der Schleife nicht 0 erreicht hat die Funktion nicht mal einen definierten Rückgabewert! Also, was soll das?


F34r0fTh3D4rk - Mo 21.03.05 14:38


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
Function WortExtrahieren(Text: string; Wort: Word; Trennzeichen: Char): string;  
var  
 p  : PChar;  
begin  
  p  := PChar(Text);  
  repeat  
    Dec(Wort);  
    while p^ <> Trennzeichen do  
    begin  
      if Wort = 0 then  
       Result := Result + p^;  
      Inc(p);  
    end;  
    if Wort = 0 then Exit;  
    Inc(p);  
  until p^ = Trennzeichen;  
end

//Beispiel
str:= 'Hallo;Du;Da';
str2:= WortExtrahieren(str, 1';');
//Dann ist str2 = Hallo


delfiphan - Mo 21.03.05 14:44

Mit dem Beispiel wird's schon viel klarer. :)
Scheint zu funktionieren, nur: Das letzte Wort lässt sich nicht korrekt extrahieren:

Delphi-Quelltext
1:
WortExtrahieren('a;b;c',3,';'); // Der zurückgegebene String ist 100 Zeichen gross!                    

Und: Wie weiss ich eigentlich, dass ich am Ende des Strings bin?
WortExtrahieren('a;b;c',4,';');gibt eine Endlosschleife. Ausserdem kann das String auch mal leer sein...


F34r0fTh3D4rk - Mo 21.03.05 14:47

ich habe gerade einen fehler in delphi enddeckt, ich habe ein wort per random auswählen und als showmessage anzeigen lassen, wenn das letzte wort ausgewählt wurde kommt kein msg fenster sondern ein formular mit unendlicher breite und links oben ein label auf dem das wort steht, ok button fehlt und X lässt sich auch net erreichen, weils nach rechts hin unendlich lang ist. :shock:


retnyg - Mo 21.03.05 14:48

wenn ich das richtig sehe wird bei dunkelfürchters code einfach der pointer erhöht, was ein speicherleck erzeugt, der PChar kann nicht mehr freigegeben werden weil der pointer nicht mehr passt


F34r0fTh3D4rk - Mo 21.03.05 14:50

ich glaube der fehler, dass das letzte wort net ausgelesen wird lässt sich so beheben:

Delphi-Quelltext
1:
str:= 'Hallo;Du;Da;';                    


delfiphan - Mo 21.03.05 14:52

Wollt ich soeben auch schreiben. @F34r0fTh3D4rk: Das ist kein Delphi-Fehler, sondern ein Folgefehler wegen deiner Prozedur. Auch wenn der Pointer korrekt vernichtet würde spuckt deine Prozedur Unsinn beim letzten Wort aus: Du testest nirgends auf das Ende des Strings!!

//Edit: Hab deinen Post erst jetzt gelesen. Lässt sich so nur bedingt beheben... Das ist doch keine Lösung ;) Woher soll man überhaupt wissen, wieviele Einträge der String hat? Die Strings kommen aus einer Datei, da kann eine Zeile auch mal leer sein!!

Wie dem auch sei: Die ursprüngliche Frage ist schon längst beantwortet. Lassen wir's sein!


F34r0fTh3D4rk - Mo 21.03.05 15:04

ich habs gerade so versucht, das ist aber schwachsinn, wie kann ich denn in meinem code das ende des strings abfragen ohne extra noch einen zähler einzubauen ?

Delphi-Quelltext
1:
until (p^ = Trennzeichen) or (not (ord(p^) in [1..255])); //<- das gleiche wie (p^ = #0)                    


delfiphan - Mo 21.03.05 15:08

p ist nullterminiert. Da brauchst du keinen Zähler. Du solltest aber eine while-Schleife benützen, da das erste Zeichen schon eine #0 sein kann.
Übrigens @retnyg: Gibt's wirklich ein Speicherleck? PChar liefert hier doch nur nen Zeiger auf Text zurück und alloziert keinen neuen Speicher.

//Edits 1,2: Sorry, komische Satzkonstruktionen geflickt ;)


F34r0fTh3D4rk - Mo 21.03.05 15:11

genau :D


retnyg - Mo 21.03.05 15:16

Zitat:

Delphi-Quelltext
1:
2:
3:
 p  : PChar;  
begin  
  p  := PChar(Text);

hier wird doch ein neuer PCHar erstellt, und der wert von text reinkopiert. danach wird der pointer "verbogen", wie soll der kompiler wissen was er nun wieder freigeben kann ?


opfer.der.genauigkeit - Mo 21.03.05 15:23

Wieso sollte das n Speicherleck erzeugen?

p ist der Zeiger auf den String.
Der String wird von Delphi eh seperat behandelt.

p = pchar(s) und d.h. p ist der zeiger von s .. wird also s, von Delphi, freigegeben ist der pointer eh müll..
Sinnvoll wäre da noch p = nil. Schöner wär's allerdings.

Versucht mal den pointer so freizugegeben Dispose(p) .. viel Spaß mit der Exception.


F34r0fTh3D4rk - Mo 21.03.05 15:25

Zitat:

Sinnvoll wäre da noch p = nil. Schöner wär's allerdings.

wo ?


retnyg - Mo 21.03.05 15:26

user profile iconopfer.der.genauigkeit hat folgendes geschrieben:
Wieso sollte das n Speicherleck erzeugen?

p ist der Zeiger auf den String.
Der String wird von Delphi eh seperat behandelt.

wäre p der zeiger auf einen string, würde das so aussehen:

Delphi-Quelltext
1:
2:
3:
 p  : ^string;  
begin  
  p  := @Text;


F34r0fTh3D4rk - Mo 21.03.05 15:26

so funktionier das aber leider net


opfer.der.genauigkeit - Mo 21.03.05 15:45

Ok,

ich seh's ein.. blöd formuliert. :oops:

Du hast recht retnyg.. ist kein zeiger auf string.

*sfz*

Ok, besser ausgedrückt.

p := pchar(s);
Ein PChar ist ein Zeiger auf einen nullterminierten String mit Zeichen des Typs Char.

p muß und kann nicht freigegeben werden in diesem fall, da s von delphi eh freigegeben wird.
p zeigt aber weiterhin auf den Text, da Delphi den Speicher nicht mit Nullen vollschreibt, sondern nur die Referenz von s tötet.

Naja.. deshalb p := nil am Ende setzen, sofern man das möchte.

Würde man z.B. das hier machen:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
..
  private
    globalP: pchar;
..
var
  s: string;
  p: pchar;
begin
  s := 'test';
  p := pchar(s);
  globalP := p;
end;

procedure irgendwas;
begin
  showmessage(globalp); // hier wird noch test als resultat erscheinen.. wenn man glück hat. :) 
end;


Naja, ich hoffe meine Aussagen stimmen, sonst muß ich mich wohl heute einbuddeln und nochmal das Thema mit den Zeigern durchkämpfen. :roll:


F34r0fTh3D4rk - Mo 21.03.05 15:49

oha, bin ich dumm, so muss es lauten:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
Function WortExtrahieren(Text: string; Wort: Word; Trennzeichen: Char): string;
var
 p  : PChar;
begin
  p:= PChar(Text);
  repeat
    Dec(Wort);
    while p^ <> Trennzeichen do
    begin
      if Wort = 0 then
        Result := Result + p^;
      if p^ = #0 then
        Exit;
      Inc(p);
    end;
    if Wort = 0 then
      Exit;
    Inc(p);
  until (p^ = Trennzeichen);
  P:= nil;
end;


SchelmVomElm - Mo 21.03.05 15:53

p ist ein Zeiger auf das erste Zeichen des Strings.
Der Speicher für den gesammten nullterm. string ist reserviert.
Nach der Benutzung gibt Delphi den Speicher wieder frei.
Und zwar von p bis #0. p hat sich aber verändert und ob der Compiler das merkt und entsprechend handelt ist hier die Frage!
Durchaus diskussionswürdig wie ich finde - ich glaub das gibt 'nen Leck!


F34r0fTh3D4rk - Mo 21.03.05 15:55

hier gehn die meinungen auseinander, wo ist der beweis ?


retnyg - Mo 21.03.05 16:03


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
procedure TForm1.Button1Click(Sender: TObject);
var  s: string;
     p:pchar;
     pp, ps: pointer;
begin
  s:='bla';
  p:=pchar(s);
  ps:=@s;
  pp:=@p;
  if ps <> pp then showmessage('uneven');
end;

hier dran sieht man, dass die funktion PChar(String) zuerst einen neuen, null-terminierten string erzeugt und anschliessend darauf zeigt, es wird also nicht einfach ein pointer auf den bereits existierenden string erstellt.


F34r0fTh3D4rk - Mo 21.03.05 16:05

ja, wird dieser denn aber nicht trotzdem freigegeben ?


SchelmVomElm - Mo 21.03.05 16:07

Wenn ich einen hätte würd ich ihn auch posten. Und wenn man es schon nicht besser weiss, würde ich erst recht davon ausgehen dass ein Speicherleck gibt und mich drum kümmern. Gehört sich einfach so.


opfer.der.genauigkeit - Mo 21.03.05 16:07

Aber mit @ holst du dir den Offset der variable.
Klar, daß @p dann nicht gleich @s, das sind zwei völlig verschiedene Variablen.

Um so mehr, wird Delphi sich selbstständig um p kümmern und dabei spielt es keine Rolle wohin der Zeiger zeigt.

Achja, ganz nebenbei hier mal n Auszug aus der Hilfe:
Zitat:

Ein PChar ist ein Zeiger auf einen nullterminierten String mit Zeichen des Typs Char. Jeder der drei Zeichentypen besitzt einen integrierten Zeigertyp:

PChar ist ein Zeiger auf einen nullterminierten String mit 8-Bit-Zeichen.
PAnsiChar ist ein Zeiger auf einen nullterminierten String mit 8-Bit-Zeichen.
PWideChar ist ein Zeiger auf einen nullterminierten String mit 16-Bit-Zeichen.

PChars bilden mit kurzen Strings den ursprünglichen String-Typ von Object Pascal. Sie wurden als Kompatibilitätstyp zur Sprache C und zur Windows-API eingeführt.


//Edit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  s: string;
  p: pchar;
begin
  s := 'test';
  p := pchar(s);

  if pointer(p) = pointer(s) then showmessage('gleich');
end;


Ich verpeil eh grad alles.. *ne kippe rauchen geht*


SchelmVomElm - Mo 21.03.05 16:13

user profile iconopfer.der.genauigkeit hat folgendes geschrieben:
Aber mit @ holst du dir den Offset der variable.
Klar, daß @p dann nicht gleich @s, das sind zwei völlig verschiedene Variablen.

Um so mehr, wird Delphi sich selbstständig um p kümmern und dabei spielt es keine Rolle wohin der Zeiger zeigt.

Achja, ganz nebenbei hier mal n Auszug aus der Hilfe:
Zitat:

Ein PChar ist ein Zeiger auf einen nullterminierten String mit Zeichen des Typs Char. Jeder der drei Zeichentypen besitzt einen integrierten Zeigertyp:

PChar ist ein Zeiger auf einen nullterminierten String mit 8-Bit-Zeichen.
PAnsiChar ist ein Zeiger auf einen nullterminierten String mit 8-Bit-Zeichen.
PWideChar ist ein Zeiger auf einen nullterminierten String mit 16-Bit-Zeichen.

PChars bilden mit kurzen Strings den ursprünglichen String-Typ von Object Pascal. Sie wurden als Kompatibilitätstyp zur Sprache C und zur Windows-API eingeführt.


//Edit:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
var
  s: string;
  p: pchar;
begin
  s := 'test';
  p := pchar(s);

  if pointer(p) = pointer(s) then showmessage('gleich');
end;


Ich verpeil eh grad alles.. *ne kippe rauchen geht*



Der Sinn dieses Postings ist mir nicht klar - auch wenn ich mir dabei Zirkusmusik vorstelle! :mrgreen:


opfer.der.genauigkeit - Mo 21.03.05 16:30

Eigentlich bezieht sich das auf die letzte Aussage von retnyg
und soll ausdrücken, daß p hier nicht freigegeben werden muß.


retnyg - Mo 21.03.05 16:35

variablen werden vom delphi-compiler von haus aus selber freigegeben, mit Dispose gibt man Objekte frei.
aber woher soll der compiler wissen, wo der freizugebende pchar im speicher ist, wenn der zeiger drauf verändert wird ? ich glaub nicht dass für jede variable zwei pointer gespeichert werden.


delfiphan - Mo 21.03.05 17:11

Es kommt drauf an, wie der String gespeichert ist. Bei "hardcoded" Strings ist es anders als bei Strings, die intern mit GetMem erzeugt wurden. Hier einige Beispiele, die das verdeutlichen:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
Var
 S : String;
 P : PChar;
begin
 S := 'abcdefg'// hardcoded
 P := PChar(S);  // hier wird eine Kopie erstellt
 S[2] := 'B';
 ShowMessage(P); // abcdefg

 S := 'abcd';    // hardcoded
 S := S + 'efg'// hier wird eine Kopie des Strings erstellt (mit GetMem)
 P := PChar(S);  // hier gibt's keine erneute Kopie mehr
 S[2] := 'B';
 ShowMessage(P); // aBcdefg
end;
Siehe z.B. auch hier: Reverse [http://www.delphi-forum.de/viewtopic.php?p=222367#222367] (Hier wird ein PChar-Pointer ebenso verändert).
Ich wage mal zu behaupten, dass bei nicht hardgecodeten Strings gilt: p := PChar(S) ist äquivalent zu p := @S[1].
Könnte mich aber täuschen, das hier basiert lediglich auf Beobachtungen....

Übrigens hab ich oben (weiss nicht mehr wo) einen Denkfehler gesehn: p ist natürlich nie gleich @S!
Wenn p am gleichen Ort gespeichert ist wie S, dann gilt: p=@S[1].


opfer.der.genauigkeit - Mo 21.03.05 17:41

user profile iconretnyg hat folgendes geschrieben:
variablen werden vom delphi-compiler von haus aus selber freigegeben.


Jup, und was anderes als ne Variable ist es nicht.

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:
var
  p1,
  p2,
  p3,
  p4: pointer;
  pc: pchar;
  s : string;
  c1,
  c2: char;
begin
{
pc ist unsere variable, die den zeiger kennt.

daß pc vom typen pchar also der enthaltene zeiger auf einen char zeigt, ist dabei unerheblich.

@pc gibt den offset auf die variable p, also unseren eigentlichen pointer auf pc.
pointer(pc) macht nen typcast in diesem fall und gibt uns den zeiger auf den char zurück.
also gilt pchar(pointer(pc)) = pc;
}

  s := 'TEST';

  pc := pchar(s);

  // p1 = p2 -> p1^ = p2^ = s[1];
  p1 := pointer(s); // zeiger von s -> char(p1^) = 'T'
  p2 := pointer(pc); // zeiger von pc -> char(p2^) = 'T'

  p3 := @s; // offset
  if p1 = pointer(p3^) then showmessage('gleich');
  p4 := @pc; // offset
  if p2 = pointer(p4^) then showmessage('gleich');

  // hier der beweis, daß der wert in der variable verändert wird,
  // aber der offset von pc nicht
  inc(pc);
  if @pc = p4 then showmessage('gleich');

  // ergo muß der compiler nix machen außer pc wie eine normale variable
  // z.B. integer, string etc. zu behandeln
end;


Moderiert von user profile iconTino: Delphi-Tags hinzugefügt.


SchelmVomElm - Mo 21.03.05 17:42

Zitat:
p := PChar(S) ist äquivalent zu p := @S[1]


Also wenn Du mit S einen Pascal string meinst kann das nicht sein, denn der muss keine Nullterminierung enthalten!


Delphi-Quelltext
1:
2:
S := S + #0;
p := @S[1];


delfiphan - Mo 21.03.05 17:49

Der normale String (= implizit AnsiString) ist nullterminiert, obwohl du das nicht zu spüren bekommst!
Der ist unter Umständen sogar doppelt nullterminiert (wegen den WideChars!).
Wenn du's nicht glaubst, siehe system.pas!


retnyg - Mo 21.03.05 17:56

fazit: in diesem speziellen fall wird kein speicherleck erzeugt (ausser ein hartgecodeter string wird DIREKT hergenommen, also nicht über einen prozedureaufruf), es ist aber gefährlich, zeigertypen herumzubiegen wenn man nicht genau weiss was man tut (und ich bin mir sicher dass das bei f34ar0fth3d4rk der fall war ;)), weswegen man es vermeiden sollte.
lieber manuell einen zeiger des typs pointer erstellen und dann mit diesem herumwerkeln...


F34r0fTh3D4rk - Mo 21.03.05 17:56

wenn man mehrere strings zusammenfügt, werden die nullen dann entfernt ?
kann ja sein, dass der string dadurch immer mehr bytes schluckt ?!

und der code war ja zuerst auch nur für das zeichen #0 gedacht, deswegen hats ja auch anfangs funktioniert, wenn man eigene trennzeichen nehmen will muss man das ende des strings extra abfragen, weil es nicht = trennzeichen ist


delfiphan - Mo 21.03.05 17:58

Natürlich werden die Nullen zuerst entfernt. Wenn du wüsstest, was alles abgeht, wenn man zwei Strings addiert... Du würdest staunen! ;)


F34r0fTh3D4rk - Mo 21.03.05 17:59

hatte bis jetzt noch nicht zeit und bock mir das anzuschauen, deshalb einige wissenslücken :D


delfiphan - Mo 21.03.05 18:05


Delphi-Quelltext
1:
2:
S := 'Hallo '+A+' und '+B+' '+IntToStr(I);
S := Format('Hallo %s und %s %d',[A,B,I]);

Was denkst du, welche Variante ist effizienter? Du ahnst absolut richtig ;)
Zugegeben, IntToStr ruft Format auf, aber auch ohne IntToStr ist die zweite Methode schneller....


hansa - Mo 21.03.05 19:06

Daß mit festen Feldlängen sämtliche eventuell aufgetauchten Probleme gar nicht erst auftauchen würden, das merkt wohl keiner. 8) Das einzig brauchbare, was hier steht ist der Beitrag, von Delphifan, man könne bei Zahlen ruhig ein Trennzeichen verwenden.

Nur : wo liegt der Vorteil davon überhaupt ? Mir ist niemand bekannt, der sich heute noch auf CSV einläßt, nur weil in den 50er Jahren irgendjemand mal bei 8-Bit Zeichensätzen sich ein seltenes Zeichen dafür ausgedacht hat.


delfiphan - Mo 21.03.05 19:12

Bei festen Feldlängen gibt's dafür andere Probleme: Sie sind weniger gut editierbar, und es kann vorkommen, dass man plötzlich zu wenig Pla


retnyg - Mo 21.03.05 19:14

würde ich nicht sagen, csv ist immer noch die einfachste möglichkeit ne datenbank zu erstellen.
n paar werte mit 10 zeilen code in ne csv schreiben, die kann dann mit excel geöffnet werden und man hat alle werte in den richtigen spalten.


hansa - Mo 21.03.05 19:52

Ich gebe es bald auf. Angenommen 3 Integer stehen in einer Zeile. Jede hat 10 Stellen. Ein paar Werte auslesen aus einer Textdatei geht dann so :

nicht CSV :


Delphi-Quelltext
1:
2:
3:
Zahl1 := StrToInt (copy (zeile,1,10));
Zahl2 := StrToInt (copy (zeile,11,10));
Zahl3 := StrToInt (copy (zeile,21,10));


mit CSV und keiner festen Feldlänge sähe es z.B. so aus :


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
SemikolonPos := pos (';',Zeile);
HilfStr := copy (zeile,1,SemikolonPos-1); // String bis zum ersten Semikolon umkopieren
Zahl1 := StrToInt (copy (HilfStr,1,10));
delete (zeile,1,SemikolonPos);            // bereits umgewandelten TeilString entfernen.

SemikolonPos := pos (';',Zeile);
HilfStr := copy (zeile,1,SemikolonPos-1); 
// String bis zum jetzt ersten Semikolon umkopieren
Zahl2 := StrToInt (copy (HilfStr,1,10));
delete (zeile,1,SemikolonPos);            // bereits umgewandelten TeilString entfernen.

SemikolonPos := pos (';',Zeile);
HilfStr := copy (zeile,1,SemikolonPos-1); 
// String bis zum jetzt ersten Semikolon umkopieren
Zahl3 := StrToInt (copy (HilfStr,1,10));
delete (zeile,1,SemikolonPos);            // bereits umgewandelten TeilString entfernen.


Handelt es sich nicht um Zahlen, dann hat man sowieso wohl verloren, denn man stelle sich mal vor, irgendjemand stellt aus Versehen die Tastatur auf englisch um. Er will eingeben : "östlich" und wegen geringer Schreibmaschinenkenntnisse achtet er mehr auf die Tastatur als auf das was tatsächlich da steht. Und das ist dann ";stlich".

Jetzt kommt alles mitsamt unbewußten Schreibfehlern in eine Datei und das Programm achtet nur auf ; Da ist aber jetzt einer zuviel drin und das Programm läuft nicht bis zum Ende durch. Die bewußte Eingabe des Trennzeichens ist dabei noch völlig außer acht gelassen.

Nimmt man ein anderes "Trennzeichen", so wird sich kaum was ändern. Es kracht irgendwann doch.

Das Argument mit Excel ist übrigens auch nur Theorie. Das kommt mit falschen Zeichen in einer "CSV" Datei genauso wenig zurecht, wie jedes andere Programm auch.

Der nächste Nachteil ist, daß man im Fehlerfall einen Editor getrost vergessen kann. In einer Textdatei ein ; zuviel zu entdecken ist fast unmöglich.


retnyg - Mo 21.03.05 20:07

den zwoten codeteil kannst du dir sparen wenn du die explode funktion nimmst.
klar ist csv fehleranfällig wenn in texten semikolons vorkommen können.
wenn dies aber nie der fall sein wird, kann man das getrost verwenden.
bsp.: ich hatte mal ein ping-programm geschrieben das alle hosts eines subnetzes anpingt, und dann jeweils die hostnamen, ip-adresse und roundtrip time in ein csv schreibt.
die csv dann in excel geöffnet und schon hat man eine tabellierte übersicht aller rechner im netz.


delfiphan - Mo 21.03.05 20:20

Vollständigkeitshalber möchte ich noch darauf hinweisen, dass man Tab-Separated Files sehr einfach so einlesen kann:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
Var
 a, b, c : Integer
begin
 AssignFile(F, 'test.txt');
 reset(F);
 while not eof(F) do
 begin
  Read(F, a, b, c);
  // ...
 end;
 CloseFile(F);
end;


Wie retnyg schon gesagt hat: Files, die Spalten durch ein Zeichen trennen, werden von Excel unterstützt und sind deswegen recht praktisch. Beim CSV Format kannst du deine Dateien sogar als .CSV abspeichern und durch Doppelklick direkt im Excel ansehn.


IngoD7 - Mo 21.03.05 20:27

Warum diskutieren, ob das csv-Format "im deutschsprachigen Raum" :lol: genutzt wird und was möglicherweise alles besser ist? Natürlich wird es genutzt!
Und wer es nutzt, wer also diese Dinger erstellt und liest, der sollte tunlichst das csv-Format kennen und korrekt implementieren. Es sind dabei bekanntermaßen das Semikolon ';' und die Anführungsstriche '"' gesondert zu beachten. Csv zu benutzen mag zuweilen etwas kompliziert und nicht in allen Fällen die erste Wahl sein, aber es ist möglich, durchaus noch uso (siehe Excel) und durchaus manchmal notwendig (siehe Anforderung Threadersteller).

P.S.: Ich habe mit dem csv-Format keinerlei Probleme und nutze es eigentlich immer, um Tabellen aus Excel heraus zu exportieren, wenn ich sie in eigenen Projekten weiterverarbeiten will.


hansa - Mo 21.03.05 20:43

user profile iconretnyg hat folgendes geschrieben:
...die csv dann in excel geöffnet und schon hat man eine tabellierte übersicht aller rechner im netz.


Das ist ja schön und gut, aber Excel hat mit CSV absolut nichts zu tun.


delfiphan - Mo 21.03.05 20:46

user profile iconhansa hat folgendes geschrieben:
Das ist ja schön und gut, aber Excel hat mit CSV absolut nichts zu tun.

Doch, das hat es. Exportier/Importier mal eine Exceltabelle. Wie würdest du es tun?


hansa - Mo 21.03.05 21:19

In Excel ? Wie gesagt : mit festen Feldlängen, ohne mich um die Trennzeichen kümmern zu müssen.