Archiv verlassen und diese Seite im Standarddesign anzeigen : Delphi: In einer Datei aus Unix-Enter DOS-Enter machen?
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
Gibt es einen bestimmten Grund warum du nicht unix2dos (http://en.wikipedia.org/wiki/Unix2dos) verwendest?
lg
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.
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
Das will ich aber nicht, ich will es direkt in Delphi machen.
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.
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.
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.
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 ...
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 ;).
Mein Hauptfehler war, den String in den ich lese nicht vorher mit setlength auf die richtige Größe zu bringen.
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.
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)
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.
Und, funktioniert es jetzt, wie Du es willst?Ja, und die Konvertierung geht viel schneller als ich dachte.
Würdest du mal deinen fertigen Code posten? Danke! :)
// 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
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?
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.
Ah, jetzt verstehe ich, und in AdjustLineBreaks ist dann die Funktion mit der Schleife und if-Abfrage und dem Ersetzen?
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...
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.
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.
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.
Das müsste ich mal probieren! Denn wenn ich es in ein Memo einlesen will, funktioniert es nicht.
vBulletin®, Copyright ©2000-2025, Jelsoft Enterprises Ltd.