Forum: PC-Programmierung Dateizeiger verschieben (fread)


von Philipp K. (philipp_k59)


Lesenswert?

Hi,

ich habe versucht den Dateizeiger beim einlesen einer Textdatei mit 
einer Schleife an Newline auszurichten..

Wieso funktioniert das so nicht?
1
#include <stdio.h>
2
3
int main()
4
{
5
 int intsize = 100;
6
 char *buffer[intsize];
7
 buffer[intsize - 1] = '\0';    
8
 FILE *fp = fopen ("list.txt", "r");
9
 int cnt=0;
10
  while (fread (buffer, intsize-1, 1, fp) == 1)
11
    {
12
      printf ("loop %d\n", cnt);
13
      int nlpos = intsize - 1;
14
      while (buffer[nlpos] != '\n')
15
  {
16
    nlpos--;
17
   }
18
  
19
  fseek(fp,nlpos,SEEK_CUR);
20
  buffer[nlpos + 1] = '\0';
21
}
22
}

von Roger S. (edge)


Lesenswert?

fread() hat den Zeiger ja schon verschoben, das fseek(SEEK_CUR) mit 
positivem offset geht weiter ins unentdeckte Land. Ein quick fix koennte 
ein fseek(fp, -(intsize-1), SEEK_CUR) zu beginn des loop sein.

Cheers, Roger

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Philipp K. schrieb:
> ich habe versucht den Dateizeiger beim einlesen einer Textdatei mit
> einer Schleife an Newline auszurichten..

Wenn Du Dich am Newline "ausrichten" willst, solltest Du keine 
blockorientierte Funktion verwenden, sondern eine, die sich auch am 
Newline "ausrichtet". Nennt sich fgets(). Das Lesen in Blöcken übernimmt 
stdio sowieso schon für Dich, von daher ergibt sich überhaupt keine 
Performance-Einbuße.

Darüber hinaus kann man sich den fseek-Kram sparen.

: Bearbeitet durch Moderator
von Dirk B. (dirkb2)


Lesenswert?

Welche Warnung kommt in Zeile 7 und 20 ?

Wenn du keine bekommst, ändere den Warnlevel so, dass du eine bekommst.

von Philipp K. (philipp_k59)


Lesenswert?

Danke für die Antworten.

Ich habe wohl zu später Stunde das einfachste übersehen und dann noch 
Fehler eingebaut.

Die Lösung scheint der Binarymodus zu sein.. beim abändern des Codes um 
den Fehler einzugrenzen  habe ich dann noch einen Fehler eingebaut. 
Normalerweise hatte ich die Variable Seekoffset neben nlpos 
runtergezählt.

Frank M. schrieb:
> Das Lesen in Blöcken übernimmt stdio sowieso schon für Dich, von daher
> ergibt sich überhaupt keine Performance-Einbuße.

Das ist ja nur ein Test die Chunks sind 150MB groß. Mich interessiert 
der Unterschied.

Dirk B. schrieb:
> Welche Warnung kommt in Zeile 7 und 20 ?

OK mache ich, normalerweise nur Pointer vs Integer comparison beim If.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Philipp K. schrieb:
> Die Lösung scheint der Binarymodus zu sein.

Du liest eine Text-Datei, jedenfalls hast Du das oben geschrieben.

Das Problem ist unter Windows: Im Textmodus werden die CRLF-Paare (\r\n) 
zu einen LF (\n) gemapped. Von daher kann man sich schnell in der 
Position "verzählen". Komplizierter wird es, wenn Du unter Windows eine 
Unix-Datei einliest. Da ist dann gar kein CR (\r) drin.

Umgekehrt unter Unix/Linux: Hier findet kein Mapping statt - egal ob Du 
eine Windows-Datei oder Unix-Datei einliest. Und das ist dann noch 
unabhängig, ob Du die Datei im Binär- oder Textmodus öffnest. Vorteil: 
"Verzählen" kann man sich hier nicht.

> Das ist ja nur ein Test die Chunks sind 150MB groß.

Was für Chunks? Ich dachte, es geht um eine Text-Datei?

> Mich interessiert der Unterschied.

Welcher Unterschied?

: Bearbeitet durch Moderator
von Philipp K. (philipp_k59)


Lesenswert?

Frank M. schrieb:
> Du liest eine Text-Datei, jedenfalls hast Du das oben geschrieben.

Ja, ich weiß aber schon wie das Byte aussieht, also kann ich die auch in 
Bytes lesen. Die kann aber auch 1GB groß sein. Von Byte zu Char ist ja 
jetzt nicht die Welt.

Frank M. schrieb:
> Das Problem ist unter Windows: Im Textmodus werden die CRLF-Paare (\r\n)
> zu einen LF (\n) gemapped.

Das habe ich auch schon herausgefunden, dazu noch die ganzen 
Problematiken. Fseek64 etc.

Frank M. schrieb:
> Welcher Unterschied?

Ob fgets nicht vielleicht einen Overhead produziert. Mir sind die 
Newlines nicht wichtig, für diesen Fall der Optimierung der mich 
interessiert darf zwischen zwei Newlines kein Buffer enden und kein 
buffer anfangen. Das war meine Idee.

von Dirk B. (dirkb2)


Lesenswert?

Philipp K. schrieb:
> OK mache ich, normalerweise nur Pointer vs Integer comparison beim If.

Und das wundert dich nicht?

Da hast du einen Fehler. Der ist zwar nicht in der Zeile mit dem if, 
aber führt dazu dass dein Programm nicht funktioniert.

: Bearbeitet durch User
von Philipp K. (philipp_k59)


Lesenswert?

Dirk B. schrieb:
> Da hast du einen Fehler. Der ist zwar nicht in der Zeile mit dem if,
> aber führt dazu dass dein Programm nicht funktioniert.

Naja, gewundert habe ich mich schon..

Mein eigentliches Programm lief schon, nur das fread mit fseek nicht 
dort wo ich dachte gestartet hat. Wie hier schon geschrieben wurde liegt 
das an der Implementierung von fread bei erstellen eines Dateizeigers im 
Textmodus "r" anstatt "rb". Wobei es noch andere Seiteneffekte gibt. Ich 
teste das auf onlinegdb.com, da muss man erst auf Debug umstellen damit 
man den Kern des SegFaults bekommt.

Den Fehler habe ich  bei reduzierung des eigentlichen Problems 
eingebaut.

Danke, ich werde das mal auf dem Rechner anschauen, bin gespannt.

von Philipp K. (philipp_k59)


Lesenswert?

Klappt nicht. Dann muss ich mir etwas anderes überlegen.

ftell liefert immer ein Vielfaches von buffersize egal wie ich mit 
SEEK_SET oder SEEK_CUR die Position verschiebe.

Hat das vielleicht mit einer Optimierung vom Compiler zu tun?

EDIT: scheint doch zu laufen, habe da einen Workaround gefunden.

: Bearbeitet durch User
von Mathias A. (mrdelphi)


Lesenswert?

Philipp K. schrieb:
> char *buffer[intsize];

Das ist doch ein Array von Pointern auf char, und kein Array von chars, 
oder?

von Philipp K. (philipp_k59)


Lesenswert?

Mathias A. schrieb:
> Das ist doch ein Array von Pointern auf char, und kein Array von chars,
> oder?

Ja, ups ich glaube das wars
1
#include <stdio.h>
2
3
int
4
main ()
5
{
6
  int intsize = 128;
7
  char buffer[intsize];
8
  buffer[intsize - 1] = '\0';
9
  FILE *fp = fopen ("list.txt", "rb");
10
  int cnt = 0;
11
  unsigned long seekoff = 0;
12
  while (1)
13
    {
14
      if (fread (buffer, intsize - 1, 1, fp))
15
  {
16
    cnt++;
17
18
    seekoff = ftell (fp);
19
    //  printf ("loop %d\n", cnt);
20
    int nlpos = intsize - 1;
21
    while (buffer[nlpos] != '\n')
22
      {
23
        nlpos--;
24
        seekoff = seekoff - 1UL;
25
      }
26
    //printf("%lu",seekoff);
27
    fseek (fp, seekoff, SEEK_SET);
28
    buffer[nlpos] = '\0';
29
    printf ("%s", buffer);
30
31
  }
32
      if (cnt > 5)
33
  break;
34
    }
35
  fclose (fp);
36
}

Da habe ich mich wohl bei der Fehlersuche verstrickt.. das war vorher 
eine Initialisierung mit  malloc.

: Bearbeitet durch User
von Sebastian (Gast)


Lesenswert?

Was passiert denn am Dateiende wenn nur noch weniger als 127 Bytes zu 
lesen sind?

LG, Sebastian

von Mathias A. (mrdelphi)


Lesenswert?

Philipp K. schrieb:
> Da habe ich mich wohl bei der Fehlersuche verstrickt.. das war vorher
> eine Initialisierung mit  malloc.

Kann passieren... und so ein Fehler ist dann besonders tückisch, weil es 
mal läuft und mal nicht, und immer wieder andere Fehler auftreten aber 
irgendwie keine sinnvolle Ursache zu finden ist... :-o

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

> Was passiert denn am Dateiende wenn nur noch
> weniger als 127 Bytes zu lesen sind?
Sobald fread fehlschlägt nix mehr, für sehr lange Zeit.

von Dirk B. (dirkb2)


Lesenswert?

Philipp K. schrieb:
> Mathias A. schrieb:
>> Das ist doch ein Array von Pointern auf char, und kein Array von chars,
>> oder?
>
> Ja, ups ich glaube das wars

Das ist die Ursache für die von mir erwarteten Warnungen - und die 
Aussage, dass das Programm nicht funktioniert.

Aber hey, das ist ja „nur“ eine Warnung. Die muss man nicht beachten.

von Philipp K. (philipp_k59)


Lesenswert?

Dirk B. schrieb:
> Aber hey, das ist ja „nur“ eine Warnung. Die muss man nicht beachten.

Ich hatte diese Warnung nicht.. Außerdem hab ich da zuviel 
verschlimmbessert um das Beispiel klein zu halten.

Mathias A. schrieb:
> Ursache zu finden ist... :-o

Jo.

Sebastian schrieb:
> Was passiert denn am Dateiende wenn nur noch weniger als 127 Bytes zu
> lesen sind?

Es ist ja nur ein Problembeispiel. Man muss die Dateigröße auf die 
durchlaufenen Blöcke gegenrechnen und einen definierten Abschlussblock 
der Restgröße hinter der Schleife lesen.

Meine Idee war da, die Blockgröße zur Dateigröße anzupassen und unter 
der Buffergröße nur einmal einzulesen.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Philipp K. schrieb:
> Ich hatte diese Warnung nicht..

Darum habe ich geraten, den Warnlevel zu ändern.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.