Fscanf()

hallo Leute!

Ich möchte eine Text-Datei öffnen, komme aber mit der „fscanf()“ nicht klar. Findet ihr in dem Code irgend etwas falsches?

ULONG getFileSize(const char \*path)
 {
 WIN32\_FIND\_DATAA wfd;
 wfd.nFileSizeLow = 0;
 HANDLE hFind = INVALID\_HANDLE\_VALUE;

 hFind = FindFirstFile(path, &wfd);
 if (hFind == INVALID\_HANDLE\_VALUE) { return DATEI\_MURKS ; }

 return wfd.nFileSizeLow;
 }

char\* openFile (const char \*path, ULONG &fileSize, const char \*mode="rt")
 { 
 FILE \*fp = fopen(path, mode);
 if (fp==NULL) {return 0;} 

 fileSize = getFileSize(path);
 char \*keep=0;
 keep = new char[fileSize + 2];
 if (! keep)
 {cout 
auf WIN sollte es auch so gehen:


    char\* openFileCF(const char \*path, ULONG &sizze)
     {
     HANDLE hnd = CreateFile(path, GENERIC\_READ, FILE\_SHARE\_READ, NULL, 
     OPEN\_EXISTING, FILE\_ATTRIBUTE\_NORMAL, NULL);
     //if (!hnd) { lastErr = GetLastError(); }
    
     sizze = GetFileSize(hnd, 0);
     char \*keep = new char[sizze+2];
     ULONG numRead=0;
     if( ReadFile(hnd, (char\*)keep, sizze, &numRead, 0) == ERROR\_HANDLE\_EOF )
     {cout
    Die klappen nun beide nicht. Einmal habe ich es geschafft, dass nichts als
    '=' am Bildschirm ausgegeben wurden.
    Die fread() und auch die fwrite() sind nur für Binaer-Dateien geeignet, richtig?
    Bitte um Hilfe.
    
    gegoogelt habe ich zB hier:
    http://www.cplusplus.com/reference/clibrary/cstdio/fscanf.html
    
    Ach ja: habe WIN2K, VC6, dotNet 2003.
    
    lg
    Martin B

Hallo,

Ich möchte eine Text-Datei öffnen, komme aber mit der
„fscanf()“ nicht klar. Findet ihr in dem Code irgend etwas
falsches?

„komme … nicht klar“ ist keine adequate Fehlerbeschreibung. Was für ein Verhalten erwartest du, und was passiert?

Die fread() und auch die fwrite() sind nur für Binaer-Dateien
geeignet, richtig?

Alle Daten im Computer sind Binärdaten. Du kannst damit Byte für Byte lesen bzw. schreiben, ohne auf Windows merkwürdige Vorstellung von Zeilenenden oder ähnliches eingehen zu müssen. Tatsächlich gibt es unter Linux gar keinen „Textmodus“.

Mein Ansatz bei sowas:

Datei öffnen, mit fseek() ans Ende der Datei gehen, mit ftell() die Position (und damit Länge) der Datei bestimmen, Speicher allokieren, mit ftell() wieder zurückgehen und dann mit fread() auslesen.

Vielleicht gibt es auch eine schönere Art, an die Länge der Datei zu kommen, aber so gehts auf jeden Fall :wink:

Grüße,
Moritz

Hallo Moritz,

Vielen Dank für die rasche Antwort.

Ich möchte eine Text-Datei öffnen, komme aber mit der
„fscanf()“ nicht klar. Findet ihr in dem Code irgend etwas
falsches?

„komme … nicht klar“ ist keine adequate Fehlerbeschreibung.
Was für ein Verhalten erwartest du, und was passiert?

Inhalt der TextDatei in einen char* schreiben, den am Bildschirm ausgeben. Wie gesagt, öffnen klappt noch scheinbar, aber er gibt nur den string
„==========================================================“
aus.

Die fread() und auch die fwrite() sind nur für Binaer-Dateien
geeignet, richtig?

Alle Daten im Computer sind Binärdaten. Du kannst damit Byte
für Byte lesen bzw. schreiben, ohne auf Windows merkwürdige
Vorstellung von Zeilenenden oder ähnliches eingehen zu müssen.
Tatsächlich gibt es unter Linux gar keinen „Textmodus“.

Es ist zwar ein Unterschied, ob ich für ein „long“ nun bis zu 11 Bytes brauche (als string), oder bloss 4. Ob ich das ‚\n‘ nun als Terminator oder als „Zahl 10 eben“ ansehe, ist auch nicht ganz das Selbe.

Aber die Unterscheidung auf WIN ist natürlich eine künstliche, meine Rede. Die Frage war nun bloss so gemeint, ob ich mir mit dem Einstz der fread(), bei Text, etwa Probleme einhandeln würde.
Der Code passt soweit?

Grüße,
Moritz

lg
Martin B

Hallo,

Ich möchte eine Text-Datei öffnen, komme aber mit der
„fscanf()“ nicht klar. Findet ihr in dem Code irgend etwas
falsches?

„komme … nicht klar“ ist keine adequate Fehlerbeschreibung.
Was für ein Verhalten erwartest du, und was passiert?

Inhalt der TextDatei in einen char* schreiben, den am
Bildschirm ausgeben. Wie gesagt, öffnen klappt noch scheinbar,
aber er gibt nur den string
„==========================================================“
aus.

Die fread() und auch die fwrite() sind nur für Binaer-Dateien
geeignet, richtig?

Alle Daten im Computer sind Binärdaten. Du kannst damit Byte
für Byte lesen bzw. schreiben, ohne auf Windows merkwürdige
Vorstellung von Zeilenenden oder ähnliches eingehen zu müssen.
Tatsächlich gibt es unter Linux gar keinen „Textmodus“.

Es ist zwar ein Unterschied, ob ich für ein „long“ nun bis zu
11 Bytes brauche (als string), oder bloss 4.

Richtig. Aber wenn du es nur Zeichen für Zeichen wiedergeben willst, musst du nichts konvertieren.

Ob ich das ‚\n‘
nun als Terminator oder als „Zahl 10 eben“ ansehe, ist auch
nicht ganz das Selbe.

Doch, weil sich in C char und int nicht unterscheiden, außer durch ihre mögliche Länge.

Aber die Unterscheidung auf WIN ist natürlich eine künstliche,
meine Rede. Die Frage war nun bloss so gemeint, ob ich mir mit
dem Einstz der fread(), bei Text, etwa Probleme einhandeln
würde.

Tust du nicht.

Der Code passt soweit?

ich habe ihn ehrlich gesagt nicht verstanden, und finde, dass er für eine so einfache Aufgabe viel zu kompliziert ist. Meinen Vorschlag, wie man da einfacher machen kann habe ich ja schon geschrieben, wenn du ein Code-Beispiel willst kann ich dir gerne eines geben.

Aber vermutlich passt der Code nicht, weil er nicht das tut, was er soll.

Grüße,
Moritz

hallo Moritz!

…wenn du ein Code-Beispiel willst kann ich
dir gerne eines geben.

ja, bitte!

Aber vermutlich passt der Code nicht, weil er nicht das tut,
was er soll.

Kann man wohl sagen, :wink:

Grüße,
Moritz

lg
Martin B

Hallo,

…wenn du ein Code-Beispiel willst kann ich
dir gerne eines geben.

ja, bitte!

#include 
#include 

int main(int argc, char\*\* argv){
 long length;
 char \*buffer;
 FILE \*fh = fopen("test.c", "rb");
 if (!fh) {
 exit(1);
 }
 fseek(fh, 0, SEEK\_END);
 length = ftell(fh);
 fseek(fh, 0, SEEK\_SET);

 buffer = (char\*) malloc(length \* sizeof(char));
 if (!buffer) {
 exit(2);
 }
 fread(buffer, (size\_t) length, 1, fh);
 fclose(fh);
 printf("%s", buffer);
 return 0;
}

Ich habe immer noch den Verdacht, dass das ermitteln der Dateilänge deutlich einfacher gehen müsste, aber so tuts auf jeden Fall. Die Fehlerbehandlung ist natürlich nicht ausgefeilt, aber „you get the point“ :wink:

Grüße,
Moritz

Hallo Martin,

Ich möchte eine Text-Datei öffnen, komme aber mit der
„fscanf()“ nicht klar. Findet ihr in dem Code irgend etwas
falsches?

Mir ist nicht ganz klar, was Du überhaupt
bezweckst. Du willst eine Datei in einen
String einlesen? Und Du meinst, dass Du
ein Problem hättest, je nachdem, ob Du
„CR“ oder „CR/LF“ als Zeilenenden hast?

Bei Einlesen sollte das erstmal nicht
interessieren. Du kannst Dir eine Funktion
bauen, welche „hinterher“ die Zeilenenden
nach Wunsch anpasst. Ich habe mal ein
Ausgangsbeispiel zugefügt, dass Du Dir
vielleicht an Deine Problemstellung
anpassen kannst (für Visual C++ 6).

#include 
#include 
#include 
#include 

 unsigned long getFileSize(const char \*filepath)
{
 struct \_stat buf; 
 int s = \_stat( filepath, &buf );
 assert(s == 0 );
 return buf.st\_size; 
}

 char\* readFile(const char \*filepath, unsigned long nBytes, const char \*mode="rb")
{
 FILE \*fp;
 char \*keep = new char[nBytes + 1]; assert(keep != 0);
 memset(keep, 0, nBytes+1);
 if( (fp = fopen(filepath, mode)) != 0 ) {
 fread(keep, sizeof(char), nBytes, fp);
 fclose(fp);
 return keep;
 }
 else {
 perror(filepath);
 return 0;
 }
}

 size\_t canonizeLineEndings(char \*keep)
{
 char \*p = keep;
 while( \*p && (p=strstr(p, "\xD\xA")) ) strcpy(p, p+1); 
 return strlen(keep);
}

int main()
{
 char \* text = readFile( "stuff.txt", getFileSize("stuff.txt") );
 size\_t len = strlen(text);
 int n = canonizeLineEndings(text);
 return 0;
}

Grüße

CMБ

Hallo Moritz!

Ich habe immer noch den Verdacht, dass das ermitteln der
Dateilänge deutlich einfacher gehen müsste,

Bei WIN müsste wohl alles viel einfacher gehen…

aber so tuts auf jeden Fall.

Glaube ich auch. Vielen Dank, bis zum nächsten Mal!
lg
Martin B

Hallo Semjon!

Vielen Dank für Deine Mühe! Ich kannte das nicht mit _stat.

Mir ist nicht ganz klar, was Du überhaupt
bezweckst. Du willst eine Datei in einen
String einlesen?

Ja genau, und den dann auf dem Bildschirm ausgeben, um die Funktion zu testen. Ich möchte mir einen Code-Parser basteln. Ich finde es aber auch praktisch, wenn man das Ganze im Speicher hat.

Und Du meinst, dass Du
ein Problem hättest, je nachdem, ob Du
„CR“ oder „CR/LF“ als Zeilenenden hast?

Nein, gar nicht. Ein Problem ist nur, wenn ich nichts als

====================================

auf den Bildschirm kriege.

Ich habe Deinen Code mal laufen lassen, mit verschiedenen Dateien.
In der getFileSize()

 int s = \_stat( filepath, &buf );
assert(s == 0 );

hat mir das assert angeschlagen:

assertion failed (s == 0)
abnormal program termination

MSDN sagt nun auch, die „_stat“ gibt 0 im Erfolgsfall zurueck, aber irgendwas klappt da nicht.

Das „“\xD\xA"" kenne ich Null - ist das Unicode (der Win-Editor hat sowas gemurmelt)?
Steht das für „\r\n“?

BTW: habe jetzt eine alte Version meiner openFile() gefunden, die
liest ein mit:

 ULONG i=0;
 char ch=32;
 while (ch != EOF)
 {ch = (char)fgetc(fp);
 keep[i++] = ch;
 }

==> Da zeigt mir das i ja auch die Groesse an, .

Das klappt nun wieder fein ( @Moritz:dein Code auch! ).
Das war nur die fscanf(), die nicht wollen hat.
Seltsam auch, dass die keine Groesse des Puffers braucht.

Ich werde nochmal gucken, aber heute bin ich zu müde, uahhh…
Nochmal vielen Dank!

lg
Martin B

strstr
Hallo Semjon!

Das raffe ich nicht wirklich, wie das funktioniert:

 size\_t canonizeLineEndings(char \*keep)
{
 char \*p = keep;
 while( \*p && (p=strstr(p, "\xD\xA")) ) strcpy(p, p+1);
 return strlen(keep);
}

Der Zweck ist also, das ‚\r‘ raus zu filtern?

Das MSDN scheibt über strstr():

Return Value
Each of these functions returns a pointer to the first occurrence of strCharSet in string, or NULL if strCharSet does not appear in string. If strCharSet points to a string of zero length, the function returns string.

Das heisst, die strstr() gibt Null zurück, sobald sie das Datei-Ende erreicht hat, richtig?

Mit

strcpy(p, p+1);

wird jeweils die gesamte restlich Datei um eines nach links kopiert, und dabei das ‚\r‘ mit dem ‚\n‘ überschrieben, richtig?

Da die strstr() bei Erfolg die Adresse des Tokens zurück gibt, könnte man das wohl auch so schreiben:

 size\_t canonizeLineEndings(char \*keep)
{
 char \*p = keep;
 while(p = strstr(p, "\r\n"))
 {
 strcpy(p, p+1);
 p++;
 } 
 return strlen(keep);
}

Die Null am Ende von „keep“ rückt ebenfalls nach, so kriegst Du am Ende die korrekte strlen()?

lg
Martin B

Hallo Martin

Das raffe ich nicht wirklich, wie das funktioniert:

size_t canonizeLineEndings(char *keep)
{
char *p = keep;
while( *p && (p=strstr(p, „\xD\xA“)) ) strcpy(p, p+1);
return strlen(keep);
}

Der Zweck ist also, das ‚\r‘ raus zu filtern?

Hier meine „unobfuscated“ Version:

 size\_t canonizeLineEndings(char \*keep)
{
 char \*p = keep;

 while( p ) { // prevent null pointer assignment
 p = strstr( p, "\r\n" );
 if( p != 0 ) 
 strcpy( p, p+1 ); 
 else
 break;
 }
 return p ? strlen( keep ) : 0;
}

Das heisst, die strstr() gibt Null zurück, sobald sie das
Datei-Ende erreicht hat, richtig?
Mit

strcpy(p, p+1);

wird jeweils die gesamte
restlich Datei um eines nach links kopiert, und dabei das ‚\r‘
mit dem ‚\n‘ überschrieben, richtig?
Da die strstr() bei Erfolg die Adresse des Tokens zurück gibt,
könnte man das wohl auch so schreiben:

size_t canonizeLineEndings(char *keep)
{
char *p = keep;
while(p = strstr(p, „\r\n“))
{
strcpy(p, p+1);
p++;
}
return strlen(keep);
}

Die Null am Ende von „keep“ rückt ebenfalls nach, so
kriegst Du am Ende die korrekte strlen()?

Genau! (aber es geht auch ohne p++). Ich hatte
bei meiner ursprünglichen Version intendiert, den
Fall p==0 abzufangen (das aber falsch gemacht),
ich hätte schreiben müssen:

 size\_t canonizeLineEndings1(char \*keep)
{
 char \*p = keep;
 while( p && (p=strstr(p, "\r\n")) ) strcpy( p, p+1 ); 
 return p ? strlen( keep ) : 0;
}

Und – \r\n ist natürlich lesbarer
als \xD\xA, da hast Du schon Recht :wink:

Grüße

CMБ

Hallo Semjon!

Genau! (aber es geht auch ohne p++).

Natürlich: sobald das \r weg ist, rückt die strstr() ja ohnedies zum nächsten Token nach…

Besten Dank - Spasibo!
Bis zum nächsten Mal!
lg
Martin B

Ein paar Anmerkungen von meiner Seite:

Format specifier %s liest nur eine Sequenz von non-white-spaces
ein, d.h. bei Leerzeichen, Zeilenenden, etc. wird abgebrochen.
fscanf() ist also nicht geneignet, um komplette Dateien einzulesen.

fwrite() und fread() sind auch bei Textdateien verwendbar.
Auch bei diesen Funktionen werden systemabhängig zB die
Zeilenenden konvertiert.

Prinzipiell bin ich aber auch der Meinung, dass es am
unproblematischsten ist, Textdateien wie Binärdateien
zu behandeln, und sich um die Konvertierung selbst zu kümmern.

Folgendes Beispiel verdeutlicht das Problem:

#include 
#include 
#include 

static void dump\_buffer(const unsigned char\* buffer)
{
 while (buffer && \*buffer)
 {
 if (\*buffer \>= 32 && \*buffer 

Kompiliert mit VS71 erhält man folgendes Ausgabe:


    
    text before write: Dieser Text enthaelt[LF]zwei Zeilenenden[LF]
    wt: wrote 38 bytes, file has size 40
    rt: read 38 bytes (38), file has size 40
    text after read: Dieser Text enthaelt[LF]zwei Zeilenenden[LF]




Scheint alles wie erwartet zu sein. Man beachte den
Grössenunterschied zwischen Datei und Textpuffer.

Kompiliert mit gcc mit cygwin libs, erhälts du folgende Ausgabe:


    
    text before write: Dieser Text enthaelt[LF]zwei Zeilenenden[LF]
    wt: wrote 38 bytes, file has size 40
    rt: read 38 bytes (40), file has size 40
    text after read: Dieser Text enthaelt[LF]zwei Zeilenenden[LF][CR][LF]



Das ist offensichtlich kompletter Unsinn.

Markus.

Hier meine „unobfuscated“ Version:

size_t
canonizeLineEndings(char *keep)
{
char *p = keep;

while( p ) { // prevent null pointer assignment
p = strstr( p, „\r\n“ );
if( p != 0 )
strcpy( p, p+1 );
else
break;
}
return p ? strlen( keep ) : 0;
}

Dieser code kann evtl. etwas langsam werden fuer sehr grosse Dateien,
weil nach jeder Zeile der komplette Rest der Datei umkopiert wird. Bei 10000 Zeilen wird 9999mal durchschnittlich die halbe Datei
umkompiert.

Effizienter sollte eine Lese- und Schreibzeiger sein:

unsigned canonizeLineEndings(char \*input)
{
 char\* rpos = keep;
 char\* wpos = rpos;

 while (rpos && \*rpos)
 {
 if (rpos[0] == '\r' && rpos[1] == '\n')
 {
 \*wpos++ = '\n'; rpos += 2;
 }
 else
 {
 \*wpos++ = \*rpos++;
 }
 }
 \*wpos++ = 0;
 return (wpos-input);
}