Entwickler-Ecke

Dateizugriff - Textdatei lesen/schreiben


mb - So 31.08.03 00:55
Titel: Textdatei lesen/schreiben
Hallo!

Ich möchte - ganz einfach - eine Textdatei lesen, den Inhalt modifizieren und in eine andere Datei schreiben. Das tue ich - schon seit TP 5 - ganz einfach, inzwischen in Delphi etwas abgewandelt (und bis Delphi 6 hatte es auch immer geklappt):


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
AssignFile(f1, "file1.dat");
AssignFile(f2, "file2.dat");
Reset(f1);
ReWrite(f2);
while not eof(f1) do
begin
  ReadLn(f1, s);
  s := ProcessThisStringProc(s);
  WriteLn(f2, s);
end;
CloseFile(f1);
CloseFile(f2);


So in etwa (!) sieht das aus...

Das Ergebnis ist: es kommt eine Datei heraus, die dem Unix-Format ähnelt, was ich in diesem Fall an den Zeilenumbrüchen feststelle... Auch mein Texteditor fragt, ob er die Datei ins DOS/Win-Format konvertieren soll.... Ich frag mich, wie das kommt. In der Datei sind nur ganz normale ASCII-Zeichen, bzw. sogar [0..9,a..z,A..Z].

Ich probier das gerade mit der Trial von Delphi 7; ist da was anders geworden??

Ich wollte zu diesem Zweck ungern auf kompliziertere Methoden zurückgreifen... oder gibt es eine andere gute Möglichkeit??


Delete - So 31.08.03 05:58

Wie macht denn ProcessThisStringProc die Zeilenumbrüche? Ein Windows Zeilenumbruch sieht so aus: #13#10.


mb - So 31.08.03 11:03

Hallöle!

Ja, ich hätte mir denken können, das jemand nach dieser geheimnisvollen Prozedur fragt. ... Diesen Namen hatte ich einfach als Platzhalter eingesetzt. Es werden in wirklichkeit aber nur Zeichen(-ketten) meist am Anfang der Strings verändert.

Gehen wir davon aus, dass ich einfach mit der Funktion

Delphi-Quelltext
1:
s := AnsiReplaceText(s, 'fröhlich''traurig');                    

den SubString "fröhlich" in "traurig" ersetzen möchte. Dann dürften ja in einem normalen Text auch nicht die Zeilenenden ersetzt werden...
...und die Sourcedatei ist ganz klar eine DOS-formatige Datei mit den Umbrüchen (#13#10).

Und... ich muss den "Unix"-Hinweis wieder zurücknehmen. Es entsteht eine Unicode-Datei (?!) ...jedenfalls werdne pro Zeichen 16Bit verwendet!


Brueggendiek - So 31.08.03 11:13

Hallo MB!

Da solltest Du mal die Onlinehilfe (F1) befragen, ob D7 vielleicht standardmäßig für Strings Widestrings einsetzt!

Die Verwendung von Strings ist ja beim Übergang von Turbo-Pascal auf Delphi schon mal geändert worden - in TP war STRING identisch mit STRING[255] - das muß man bei D5 als "Shortstring" deklarieren, während STRING jetzt "Ansistring" ist mit bis zu 2GB, mit 8 Bit pro Zeichen.

Möglich, daß bei D7 das auf "Widestring" geändet wurde - das ist der Unicode-String mit 16 Bits pro Zeichen!

Ist natürlich nur so eine Vermutung, aber bei dem Fehlerbild kann ich mir nichts anderes vorstellen.

Nimm doch eventuell mal die Umwandlung ganz raus, so daß ein reines Kopierprogramm entsteht - vielleicht bewirkt die Umwandlung ja auch den 16-Bit-Effekt.

Gruß

Dietmar Brüggendiek


MrSaint - So 31.08.03 12:19

hmmm... mit den Zeilenumbrüchen? also ich bin der meinung, dass das einzig und allein an dem "WriteLn(f2, s);" liegt. weil das macht ja noch nen Zelenumbruch. und ich mein "ReadLn" ließt den Zeielnumbruch gar nich mit, wenn ich mich recht daran erinnere. heißt, in "s" is nacher nich am schluß ein #13#10 und auch kein #13 (wie das bei Unix aussieht). also wenns nich an diesem Unicode liegt (wovon ich (zu meiner Schande) keine ahnung hab ;) ), dann liegt das an dem WriteLn meiner Ansicht nach...



MrSaint


mb - So 31.08.03 12:29

Ja, dass es am WriteLn liegt dachte ich auch schon... Wie würdet ihr denn ne reine Textdatei auslesen und schreiben, wenn nicht mit ReadLn und WriteLn? Mit Write() kann ich ja keine untypisierte Datei schreiben, sonst hätt ich ja den Zeilenumbruch selbst dranhängen können.

Das mit AnsiString, WideString, ... werd ich noch mal prüfen.

Danke trotzdem soweit.


MrSaint - So 31.08.03 12:32

es gibt ja noch die API Funktionen. ich glaub das waren fileopen(..), filewrite(..), fileread(...). geb aber keine garantie dafür, weil ich die datei-funktinen irgendwie immer durcheinander bring ;)

MrSaint


Delete - So 31.08.03 12:44

Es muß auch mit writeln gehen. Aber kuck mal, ob D7 nicht wirklich standardmäßig WideStrings nimmt.


Klabautermann - So 31.08.03 13:03

Hallo,
Luckie hat folgendes geschrieben:
Es muß auch mit writeln gehen. Aber kuck mal, ob D7 nicht wirklich standardmäßig WideStrings nimmt.

ich habe gerade mal ein wenig in mainer D7 hilfe gestöbert. Dieses könnte für mb interessant sein:
Delphi 7 Hilfe hat folgendes geschrieben:
Das reservierte Wort string funktioniert wie ein generischer Typbezeichner:

var S: string;

Hier wird beispielsweise die Variable S für einen String erstellt. Im voreingestellten Status {$H+} interpretiert der Compiler string als AnsiString (wenn auf das reservierte Wort keine Zahl in eckigen Klammern folgt). Bei Verwendung der Direktive {$H?} wird string als ShortString interpretiert.


Danach wird nicht standarmäßig WideString verwendet.

Es gab ein paar änderungen an den StrUtils:
Delphi 7 Hilfe hat folgendes geschrieben:
Unit StrUtils

Die Unit StrUtils weist folgende Änderungen hinsichtlich der Unterstützung von Multibyte-Zeichensätzen (MBCS) auf:
  • In älteren Versionen konnte an LeftStr, RightStr und MidStr nur ein AnsiString-Parameter übergeben werden. Der Rückgabewert war ebenfalls auf den Typ AnsiString beschränkt. Multibyte-Zeichensätze (MBCS) wurden nicht unterstützt. In der neuen Version wurde jede dieser Funktionen durch ein Paar überladener Funktionen ersetzt, von denen jeweils eine AnsiString-Parameter und -Rückgabewerte und die andere WideString-Parameter und Rückgabewerte unterstützt. MBCS-Strings werden von den neuen Funktionen verarbeitet. Aufgrund dieser Änderung ist Quelltext, in dem diese Funktionen zum Speichern und Abrufen von Byte-Werten in AnsiStrings eingesetzt werden, nicht mehr funktionsfähig und muss (unter Verwendung der nachstehend beschriebenen neuen Funktionen auf Byte-Ebene) umgeschrieben werden.
  • Die neuen Funktionen LeftBStr, RightBStr und MidBStr werden für Operationen auf Byte-Ebene eingesetzt, die früher mithilfe von LeftStr, RightStr und MidStr implementiert wurden.
  • Die neuen Funktionen AnsiLeftStr, AnsiRightStr und AnsiMidStr entsprechen den neuen Funktionen AnsiStr, LeftBtr, RightStr und MidStr, sind aber im Gegensatz zu diesen nicht mit WideString-Funktionen überladen.

Die Unit StrUtils enthält die neue Funktion PosEx für die Stringsuche.

vieleicht ergeben sich dadraus konpflikte.

@MB du könntest ja sicherheitshalber mal versuchen, deinen String vor dem Schreiben zu Typecasten. Also so:

Delphi-Quelltext
1:
WriteLn(f2, AnsiString(s));                    


Gruß
Klabautermann


mb - So 31.08.03 13:34

Danke für die Antworten. Dennoch, hier der nicht ganz erfolgreiche Zwischenstatus...

Ich habe folgendes probiert...


Delphi-Quelltext
1:
2:
3:
{$H-}
var s: String;
{$H+}

...kein Erfolg


Delphi-Quelltext
1:
 var s:ShortString;                    

...kein Erfolg


Delphi-Quelltext
1:
 WriteLn(f2, AnsiString(s));                    

...kein Erfolg

Selbst, wenn ich die Datei nur kopiere mit

Delphi-Quelltext
1:
2:
ReadLn(f1, s);
WriteLn(f2, s);

...kommt nix gutes dabei raus. Es liegt also nicht an der Bearbeitung des Strings...

Da bin ich nun ehrlich gesagt etwas ratlos.

Andere Frage: Falls ich doch andere Zugriffsmethoden benutzen würde, so müsste ich die Datei dennoch Zeilenweise auslesen können; das geht aber ja AFAIK nicht mit FileWrite() und FileRead() ... oder BlockRead(), etc... oder? Ich will ja kein Array/Puffer auslesen, sondern eben immer bis zum Zeilenende. (...und das müsste m.E. einfacher gehen, als einen Puffer zu lesen und dann auf ^M^J / #13#10 o.ä zu untersuchen)


Klabautermann - So 31.08.03 14:28

Hallo,

du könntest ein Objekt vom Typ tStringList erzeugen, und dessen ReadFromFile und WriteToFile Methoden verwenden.
Über

Delphi-Quelltext
1:
MyStringlistObject[i]                    

kannst du auf die einzelnen Zeilen zugreifen.

Aber dass mit dem ReadLn, WriteLn wundert mich.

Gruß
Klabautermann


recall - So 31.08.03 23:02

Hallo,

von welchem Typ sind denn dein f1 bzw. f2 ? TextFile ?
Nicht: "file of String" , oder ??
(daran könnts liegen)

Viele Grüsse.

P.S.: Bei mir läufts einwandfrei :D .

//EDIT: Habe auch D7 (vergessen)


mb - Mo 01.09.03 09:57

Ja, meine Deklaration in diesem Bereich ist/war

Delphi-Quelltext
1:
var f1, f2: TextFile;                    


(Das muss ja auch irgendwie so sein, dass mir ein ganz blöder Fehler unterlaufen ist... ?!)

Dennoch: File of String akzeptiert Delphi auch nicht. Früher unter TP/BP war das ja mit  var f1,f2: Text; getan. Seit sich das geändert hat, habe ich es immer erfolgreich (!) mit TextFile gemacht...

Aber zurückzu "File of String". Der Compiler meckert, dass der Typ String eine Finalisierung braucht, die im File type nicht erlaubt ist.

Wenn ich stattdessen File of ShortString oder ... String[255] einsetze, dann erhalte ich in meiner ReadLn(f1, s) - Codezeile die Meldung, dass die Typen inkompatibel sind.

@Klabauterman: Danke für diesen Tipp. Ich werde den gleich mal probieren.


mb - Mo 01.09.03 10:35

Na ja, irgendwie stell ich mich wohl blöd an... Ich glaub, an so einer einfachen Sache saß ich noch nie so lange...

Also, hab umgestellt auf TStringList ... etwa so:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
 var f: TStringList;
  .
  .
  .
     f := TStringList.Create;
      try
        f.LoadFromFile(fn);
        f.SaveToFile(ChangeFileExt(fn, '.bak'));
      finally
        f.Free;
      end;


Das Ergebnis ist: die erzeugte Datei ist leer. Besser gesagt: Bei LoadFromFile passiert schlichtweg nichts, es wird kein Inhalt in die Stringliste gelesen...


mb - Mo 01.09.03 10:37

recall hat folgendes geschrieben:
P.S.: Bei mir läufts einwandfrei


Könntest Du mal einen Sourceabschnitt posten, der bei dir compiliert wird und auch "semantisch" korrekt funktioniert ?


Motzi - Mo 01.09.03 10:53

mb hat folgendes geschrieben:
Das Ergebnis ist: die erzeugte Datei ist leer. Besser gesagt: Bei LoadFromFile passiert schlichtweg nichts, es wird kein Inhalt in die Stringliste gelesen...

Existiert die Datei? Was steht in der Datei drinnen?


recall - Mo 01.09.03 12:34

@mb: Ich habe den ersten Quelltext von dir genommen (natürlich ohne s := ProcessThisStringProc(s); ), mit var f1,f2: TextFile; und das ganze in ein Button-OnClick-Ereignis getan...
Dann noch eine existierende Textdatei rausgesucht, fertig.

Hat funktioniert.
Vielleicht ist irgendwas mit deiner unit "system" nicht richtig ?!
Vielleicht neu installieren ?

Hast du Windows ME ? Da funktionierte bei mir die ini-Fileverwaltung nicht :( und auch einige andere Dateizugriffe murksten rum.
(Werde dazu noch einen Thread aufmachen ;) ).

Viele Grüsse.


barfuesser - Mo 01.09.03 12:34

@mb: Hast Du Dir schon mal die Ursprungsdatei angesehen? Nicht das in dieser Datei schon der Fehler liegt.

barfuesser


mb - Mo 01.09.03 13:15

Hallo - erst noch mal "danke" für Eure Teilnahme / Beteiligung an meinem Problem...
...irgendwie hört sich das ganze lächerlich an und dennoch finde ich echt den Fehler nicht.

Zu den Fragen:
Ja, die Datei existiert. Ich habe es auch schon mit anderen testweise probiert. Die Funktion ist ja inzwischen nur auf das kopieren (einlesen und wieder schreiben) reduziert ... selbst das funktioniert nicht.

Im Original-Quelltext sind sowieso noch weitere try-catch-Blöcke (sorry: ...except ;-)) vorhanden. Also, wenn da ein Fehler auftreten würde, müsste ich es merken (Exception).

Ratlos....


Delete - Mo 01.09.03 13:23

Beantworte doch mal bitte dir Frage von barfuesser!


Klabautermann - Mo 01.09.03 13:39

Hallo,

mb, ich habe dir gerade ein Beispielprogramm, das bei mir perfekt läuft zugemailt. Probiere es mal aus.

Für die interessierten, es handelt sich um ein Form mit einem Button und ein Memo, welches in der OnClick des Buttons folgenden Code enthällt:

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:
procedure TForm1.Button1Click(Sender: TObject);
  var
    f1, f2 : TextFile;
    s : String;
begin
  Memo1.Lines.Add('Beginne mit dem Einlesen:');
  Memo1.Lines.Add('--------------------------');
  Memo1.Lines.Add('');
  AssignFile(f1, ExtractFilePath(paramstr(0)) + 'in.txt');
  AssignFile(f2, ExtractFilePath(paramstr(0)) + 'out.txt');
  Reset(f1);
  ReWrite(f2);
  while not eof(f1) do
  begin
    ReadLn(f1, s);
    Memo1.Lines.Add(s);
//    s := ProcessThisStringProc(s);
    WriteLn(f2, s);
  end;
  CloseFile(f1);
  CloseFile(f2);
  Memo1.Lines.Add('');
  Memo1.Lines.Add('Zieldatei Lesen:');
  Memo1.Lines.Add('----------------');
  Memo1.Lines.Add('');
  Reset(f2);
  while not EOF(f2) DO BEGIN
    ReadLn(f2, s);
    Memo1.Lines.Add(s);
  END;
end;


Gruß
Klabautermann


mb - Mo 01.09.03 14:39

Luckie hat folgendes geschrieben:
Beantworte doch mal bitte dir Frage von barfuesser!


...ich dachte das hätte ich schon getan. Also, an der Datei lag es nicht.

@Klabautermann: Danke für die Mail. Ich probier es aus.


mb - Mo 01.09.03 18:16

Hallo!

So, nun endlich funktioniert es.
Das kompilierte exe-file von Klabautermann hat seinen Zweck erfüllt, die Sourcen jedoch nicht mehr, wenn ich sie selbst kompiliert habe. Ich habe zwar nie was an den Original-Delphi-Sourcen geändert, dennoch muss es irgendwie an meiner Installation gelegen haben - was ich mir gar nicht richtig erklären kann...

Ich hab Delphi noch mal "drüber gebügelt"; Einstellungen vorher gesichert und exakt so wiederhergestellt. Nun klappt es. Auch mit meinen Sourcen.

Danke für Eure Mühe.


Klabautermann - Mo 01.09.03 20:38

Sehr Misteriös...

Aber wenn es jetzt klappt ist es ja gut, und da auch sämtliche zweifel an deinen Programmierkünsten ungerechtfertigt wahren, kannst du ja heute beruigt schlafen ;).

Gruß
Klabautermann


mb - Mo 01.09.03 21:27

[quote=Klabautermann]da auch sämtliche zweifel an deinen Programmierkünsten ungerechtfertigt wahren, kannst du ja heute beruigt schlafen [/quote]
Nun ja, man sollte sich und seine Programmierkünste vielleicht besser an den "kleinen Problemen" als an großen einschätzen...

Jedenfalls habe ich so manches wirklich große Problem schon schneller gelöst, als diese doch sehr primitive Aufgabe. (Vielleicht sind Computer doch eigenwillig ? *g*)

Der Schlaf liegt jedenfalls noch in großer Ferne... :)