PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Delphi: In einer Datei aus Unix-Enter DOS-Enter machen?


aths
2008-11-14, 20:09:39
Ich will lf (line feed) zu cr lf (carriage return, line feed) umwandeln indem ich die einige MB große Datei in einen String einlese und dann mit strreplace arbeiten.

Leider klappt das Einlesen nicht.



var utfi : file ;
fs: integer;

begin

assignfile(utfi,a); // a ist ein string mit dem Dateinamen
reset(utfi,1);
fs:=filesize(utfi);
blockread(utfi,testst,fs); <--- Hier wirft er zur Laufzeit einen Fehler, Zugriffsverletzung auf eine Speicheradresse
closefile(utfi);


Auch mit file of byte klappt es nicht. Schon die filesize wird falsch übergeben (etwa 6 MB obwohl die Datei 8 MB groß ist.)

Sephiroth
2008-11-14, 21:06:57
Vielleicht ist dein Buffer testst nicht groß genug?

BlockRead ist imho auch nicht sinnvoll, weil die Datei dann als binary behandelt wird. ReadLn wäre da wohl angebrachter.
http://www.delphibasics.co.uk/RTL.asp?Name=BlockRead

Gast
2008-11-15, 13:10:16
Gibt es einen bestimmten Grund warum du nicht unix2dos (http://en.wikipedia.org/wiki/Unix2dos) verwendest?

lg

aths
2008-11-15, 14:43:49
Vielleicht ist dein Buffer testst nicht groß genug?

BlockRead ist imho auch nicht sinnvoll, weil die Datei dann als binary behandelt wird. ReadLn wäre da wohl angebrachter.
http://www.delphibasics.co.uk/RTL.asp?Name=BlockReadReadln bezieht sich ja auf die "richtigen" Enter. Um mit Readln anschließend Zeile für Zeile auszulesen, will ich erst mal die Enter konvertieren.

Gibt es einen bestimmten Grund warum du nicht unix2dos (http://en.wikipedia.org/wiki/Unix2dos) verwendest?Ich arbeite komplett unter Windows und will das einlesen in meinem Delphi-Programm so gestalten dass man vorher nix von Hand umwandeln muss. Im Moment muss ich die Datei mit wordpad.exe öffnen und speichern, bevor ich sie in Delphi verwenden kann.

Gast
2008-11-15, 18:13:35
Ich arbeite komplett unter Windows [...]
Nach kurzer Suche findet man unix2dos auch für Windows:
Eins (http://www.freeware-archiv.de/UNIX2DOS-DOS.htm)
Zwei (http://www.bastet.com/)
Drei (http://www.jostjahn.de/software/unix2do.html)
(habs allerdings nicht getestet)

Externe Programme aufrufen wird mit Delphi ja wohl hoffentlich gehen...

lg

aths
2008-11-15, 20:27:16
Das will ich aber nicht, ich will es direkt in Delphi machen.

PHuV
2008-11-16, 04:29:00
Gibt es einen bestimmten Grund warum du nicht unix2dos (http://en.wikipedia.org/wiki/Unix2dos) verwendest?

lg

Oder man verwendet gleich einen Editor wie Notepad++, UltraEdit etc., welche Dos- und Unix-Dateien verwenden kann.

@Aths, ich kenne Delphi nicht so gut, hast Du mal den Filedescriptor/-handler überprüft? Man kann Dateien in verschiedenen Modi öffnen (Textmodus, Binary etc.), und wenn man den falschen Modus auswählt, kann daraus eine falsche Berechnung entstehen.

aths
2008-11-16, 12:46:54
Oder man verwendet gleich einen Editor wie Notepad++, UltraEdit etc., welche Dos- und Unix-Dateien verwenden kann.

@Aths, ich kenne Delphi nicht so gut, hast Du mal den Filedescriptor/-handler überprüft? Man kann Dateien in verschiedenen Modi öffnen (Textmodus, Binary etc.), und wenn man den falschen Modus auswählt, kann daraus eine falsche Berechnung entstehen.Ich habe file mit record-Größe 1 und file of byte (Record-Größe ist dann automatisch 1) probiert. Beides klappt nicht.

Die CSV-Datei die ich einlese wird von einem anderen Programm erstellt. Bisher muss ich die immer in Wordpad öffnen und speichern bevor ich sie mit meinem Delphi-Programm verwenden kann. Ich will etwas einbauen dass ich zunächst prüfe, welche Enter-Kodierung genommen wird und falls es Unix-Enter sind, die Datei schnell konvertieren.

PHuV
2008-11-16, 15:47:53
Ich habe file mit record-Größe 1 und file of byte (Record-Größe ist dann automatisch 1) probiert. Beides klappt nicht.

Die CSV-Datei die ich einlese wird von einem anderen Programm erstellt. Bisher muss ich die immer in Wordpad öffnen und speichern bevor ich sie mit meinem Delphi-Programm verwenden kann. Ich will etwas einbauen dass ich zunächst prüfe, welche Enter-Kodierung genommen wird und falls es Unix-Enter sind, die Datei schnell konvertieren.

Dann mache es doch so:

Vorlese-Routine, was die Dateien erst mal im Binärmodus annimmt. Wie ich sehen kann gibt es den Typ untypisierte Dateien (http://www.schule.de/schulen/oszhdl/gymnasium/faecher/informatik/rechnerarchitektur/dateien/pascal_dateien.htm).
Du ließt die Datei binär in einen Stringbuffer, nudelst jedes Byte durch eine Schleife, und schreibst dann in einem doppelt so großen Buffer alle gefundenen Bytes NL (0a) mit CR NL (0d 0a), die andere Bytes werden dann direkt in den neuen Buffer übernommen.
Alternativ (die saubere Lösung) zählst Du durch Vorlesen in dem Stringbuffer vorher alle gefundenen Newlines, und erweiterst den neuen Buffer um genau diese gefundene Anzahl + die ursprüngliche Bufferlänge. Dann wieder auf Index 0 setzen, und verarbeiten, wie ich es in der ersten Zeile dieses Absatzes schrieb.
Den neuen Buffer speicherst Du dann als neue Textdatei temporär ab, welche Du dann beliebig weiterverarbeiten kannst.

Sephiroth
2008-11-16, 16:27:06
Here's a program illustrating the use of FileStream (TFileStream component) to manipulate a text file. The specific task at hand is to eliminate redundant carriage return characters from a text file.
http://www.delphiforfun.org/Programs/delphi_techniques/FileFix.htm

der verwendet im zweiten teil auch readln + AdjustLineBreaks (http://delphi.about.com/library/rtl/blrtlAdjustLineBreaks.htm)

Aber nochmal zur Frage nach dem Buffer: ist der groß genug? Du liest die Datei ja komplett ein, also muss dein Buffer auch mindestens so groß sein.
Außerdem wäre das dann doch besser nicht fs-viele Lesevorgänge zu machen (weil du die RecordSize auf 1 byte festgelegt hast), sondern gleich mehr Bytes auf einmal zu lesen. Dann muss deiner Buffer aber wenigstens ein vielfaches der RecordSize sein.

PHuV
2008-11-16, 16:47:44
der verwendet im zweiten teil auch readln + AdjustLineBreaks (http://delphi.about.com/library/rtl/blrtlAdjustLineBreaks.htm)


Da steht aber Unsinn drin:


The function changes any CR characters not followed by a LF and any LF characters not preceded by a CR into CR/LF pairs. It also converts LF/CR pairs to CR/LF pairs. The LF/CR pair is common in Unix text files.


:| common in Unix text files? :| Die sollten nochmals sich genauer informieren. Unix verwendet kein CR. Den Mist hat CP/M verbrochen, den Bill Gates leider einfach übernommen hat (genau wie das beknackte \ für die Verzeichnisse).

ScottManDeath
2008-11-16, 20:49:00
Windows versteht auch / als separatoren fuer Verzeichnisse ...

aths
2008-11-17, 10:14:35
Dann mache es doch so:

Vorlese-Routine, was die Dateien erst mal im Binärmodus annimmt. Wie ich sehen kann gibt es den Typ untypisierte Dateien (http://www.schule.de/schulen/oszhdl/gymnasium/faecher/informatik/rechnerarchitektur/dateien/pascal_dateien.htm).
Du ließt die Datei binär in einen Stringbuffer, nudelst jedes Byte durch eine Schleife, und schreibst dann in einem doppelt so großen Buffer alle gefundenen Bytes NL (0a) mit CR NL (0d 0a), die andere Bytes werden dann direkt in den neuen Buffer übernommen.
Alternativ (die saubere Lösung) zählst Du durch Vorlesen in dem Stringbuffer vorher alle gefundenen Newlines, und erweiterst den neuen Buffer um genau diese gefundene Anzahl + die ursprüngliche Bufferlänge. Dann wieder auf Index 0 setzen, und verarbeiten, wie ich es in der ersten Zeile dieses Absatzes schrieb.
Den neuen Buffer speicherst Du dann als neue Textdatei temporär ab, welche Du dann beliebig weiterverarbeiten kannst.
Viel zu aufwänding / langsam. Ich will in einem Rutsch die gesamte Datei in einen String lesen und dort mit stringreplace (oder der Funktion die Sephiroth gepostet hat) die Enter-Zeichen korrigieren.

gr@fz@hL
2008-11-17, 11:48:37
Ich kenn mich in Delphi nicht aus, habe aber das gefunden:
http://www.delphipraxis.net/topic19773.html

Falls es Unsinn ist oder mit dem Problem nix zu tun hat, entschuldige ich mich und verweise auf meinen ersten Satz ;).

aths
2008-11-17, 20:35:09
Mein Hauptfehler war, den String in den ich lese nicht vorher mit setlength auf die richtige Größe zu bringen.

PHuV
2008-11-19, 16:16:22
Windows versteht auch / als separatoren fuer Verzeichnisse ...

Nur beim Programmieren! Sobald Du in Kombination mit System-Aufrufen, Konsolen-Anweisungen bzw. Argumenten arbeitest, hast Du mit dem / wieder verloren, und mußt sie dann immer von Hand selbst korrigieren.

@aths

Und, funktioniert es jetzt, wie Du es willst?

ScottManDeath
2008-11-19, 21:57:52
Mhmmm

Das geht

c:\>cd c:/Development/

c:\Development>


Allerdings funktioniert die Tab Completion nicht fuer Unterverzeichnisse von Development mit /. Mit \ gehts aber.

PHuV
2008-11-19, 22:42:05
Mhmmm

Das geht

c:\>cd c:/Development/

c:\Development>


Allerdings funktioniert die Tab Completion nicht fuer Unterverzeichnisse von Development mit /. Mit \ gehts aber.

Tatsache, Du hast recht. :tongue: Es geht aber immer nur von Root aus, sobald man in einem Unterverzeichnis ist, funktioniert es schon nicht mehr, z.B.:


c:\>cd c:/Development/Source

c:\Development\Source>

c:\Development\Source>cd c:/Development
Das System kann den angegebenen Pfad nicht finden.



Programmtechnisch verlassen würde ich mich nicht darauf.

ScottManDeath
2008-11-19, 23:14:46
Ich nehm eh boost::filesystem und "/", da ist es Rille ;)

Abe Ghiran
2008-11-19, 23:47:25
Den Mist hat CP/M verbrochen, den Bill Gates leider einfach übernommen hat (genau wie das beknackte \ für die Verzeichnisse).
Why is the line terminator CR+LF? (http://blogs.msdn.com/oldnewthing/archive/2004/03/18/91899.aspx)

PHuV
2008-11-20, 11:14:16
Why is the line terminator CR+LF? (http://blogs.msdn.com/oldnewthing/archive/2004/03/18/91899.aspx)

Na ja, sie hätten ja auch die Unix-Konvention verwenden können, hätte zudem wieder, wie bei Unix, ein Byte gespart.

aths
2008-11-20, 19:17:46
Und, funktioniert es jetzt, wie Du es willst?Ja, und die Konvertierung geht viel schneller als ich dachte.

Gast
2008-11-21, 11:20:10
Würdest du mal deinen fertigen Code posten? Danke! :)

aths
2008-11-22, 12:04:26
// String a wird an die Funtkion übergeben und enthält den Dateinamen

var fs,gb: integer; // filesize und gelesene Byte, letztere Variable wird im Programm nicht ausgewertet
utfi : file of byte; // ursprünglich untypisierte Datei, daher der Variablenname
testst:ansistring;

begin
...

assignfile(utfi,a);
reset(utfi);
setlength(testst,2000);
blockread(utfi,testst[1],2000,gb); // keine Zeile meiner Datei ist länger als 1000 Zeichen
closefile(utfi);
if pos(chr(13)+chr(10),testst)=0 then begin //wenn kein DOS-Enter drin ist, muss konvertiert werden
form1.panel1.caption:='Datei wird konvertiert ...';
application.processmessages;
assignfile(utfi,a);
reset(utfi);
fs:=filesize(utfi);
setlength(testst,fs);
blockread(utfi,testst[1],fs,gb); // wichtig ist das [1]
closefile(utfi);
testst:=AdjustLineBreaks(testst);
assignfile(utfi,a);
rewrite(utfi);
blockwrite(utfi,testst[1],length(testst)); // Das [1] nicht vergessen!
closefile(utfi);

... // Jetzt kann die Datei Zeile für Zeile mit Readln gelesen werden

PHuV
2008-11-24, 10:14:54
blockread(utfi,testst[1],2000,gb); // keine Zeile meiner Datei ist länger als 1000 Zeichen
closefile(utfi);


Blöde Frage. Wenn ich es richtig lesen (bin ja kein Delphi-Experte) ließt der blockread eine Datei komplett in den Buffer. Was meinst Du dann mit "keine Zeile meiner Datei ist größer..". Ist jetzt Deine Datei nicht größer 2000 Zeichen?

aths
2008-11-24, 11:08:40
Blöde Frage. Wenn ich es richtig lesen (bin ja kein Delphi-Experte) ließt der blockread eine Datei komplett in den Buffer. Was meinst Du dann mit "keine Zeile meiner Datei ist größer..". Ist jetzt Deine Datei nicht größer 2000 Zeichen?Die Datei ist einige MB groß und besteht aus Textzeilen. Ich weiß von der ersten Zeile, da sie die (festgelegten) Spalten-Bezeichner speichert, dass sie auf jeden Fall kleiner als 2000 Zeichen ist. Also reicht es wenn ich in dem Bereich erst mal nachgucke, ob sich dort ein DOS-Enter findet. Wenn ja, mache ich nichts. Wenn sich kein DOS-Enter findet, lese ich noch mal die gesamte Datei, konvertiere die Enter und schreibe das Ergebnis zurück.

PHuV
2008-11-25, 14:10:46
Ah, jetzt verstehe ich, und in AdjustLineBreaks ist dann die Funktion mit der Schleife und if-Abfrage und dem Ersetzen?

Gast
2008-11-25, 17:04:16
procedure BlockRead(var F: File; var Buf; Count: Integer [; var AmtTransferred: Integer]);

Description:
F is an untyped file variable, Buf is any variable, Count is an expression of type Integer, and AmtTransferred is an optional variable of type Integer.
BlockRead reads Count or fewer records from the file F into memory, starting at the first byte occupied by Buf. The actual number of complete records read (less than or equal to Count) is returned in AmtTransferred.
The entire transferred block occupies at most Count * RecSize bytes. RecSize is the record size specified when the file was opened (or 128 if the record size was not specified).

If the entire block was transferred, AmtTransferred is equal to Count.



Meiner Meinung nach sollte man die Finger von dieser Funktion lassen (und statt dessen gleich ReadFile() nutzen) und vor allem für "Buf" keinen String übergeben (den man mit [1] referenzieren muss da [0] ein interner Zeiger auf Ort und Länge des Strings ist). Die Fehlerquellen an dieser Stelle sind gewaltig, vor allem beim Einlesen von Daten die den String "sprengen" ... Stichwort Pufferüberlauf. Man schaue sich mal zum Verständnis der Probleme die "system.pas" zu dem Thema an...

aths
2008-11-28, 10:53:34
Ah, jetzt verstehe ich, und in AdjustLineBreaks ist dann die Funktion mit der Schleife und if-Abfrage und dem Ersetzen?AdjustLineBreaks wird ohne Schleife genutzt. Der Befehl macht aus dem gesamten String einen mit richtigen Enter-Repräsentationen.

PHuV
2008-11-28, 23:48:07
AdjustLineBreaks wird ohne Schleife genutzt. Der Befehl macht aus dem gesamten String einen mit richtigen Enter-Repräsentationen.

Aha, und wie genau? Der muß doch vorher wissen, was das ursprüngliche Trennzeichen war (0a), damit er das neue reinmachen bzw. ersetzen kann?

Markus89
2008-11-29, 00:13:35
Ohne jetzt seine Lösung zu kennen würde ich StringReplace(str, #10, #13#10, [rfReplaceAll]); benutzen.

aths
2008-11-29, 15:29:42
Aha, und wie genau? Der muß doch vorher wissen, was das ursprüngliche Trennzeichen war (0a), damit er das neue reinmachen bzw. ersetzen kann?Er ersetzt automatisch jedes einzelne 0D und jedes einzelne 0A zu 0D0A. (Eine fertige 0D-0A-Folge wird nicht angetastet.)

Falls man das Programm auf Linux kompiliert, geht die Umwandlung andersrum.

grandmasterw
2008-11-29, 15:46:26
Hab jetzt schon länger nix mehr gemacht, aber ich bild mir ein, dass die Funktion TStrings.LoadFromFile automagisch beide Versionen von Zeilenumbrüchen richtig liest, und du deine Zeilen dann auch gleich in einer Liste hast.

aths
2008-11-29, 18:06:18
Das müsste ich mal probieren! Denn wenn ich es in ein Memo einlesen will, funktioniert es nicht.