[C] Textdatei in String speichern

Gast #2478679
Lesenswert?

Guten Abend,

ich versuche im Moment ein Programm für die PlayStation Portable zu zu 
schreiben. Dabei möchte ich eine ganze Textdatei, ohne die 
Formatierungszeichen (EOF und Zeilenumbruch) in einen String speichern 
um ihn später weiterverarbeiten zu können.

Leider hängt sich die PlayStation sofort nach dem starten des Programms 
auf. Habt Ihr eine Idee woran es liegen könnte?

Hier mein Code
1
int main() {
2
    
3
   SetupCallbacks();
4
   pspDebugScreenInit();
5
  
6
   int Zeichenanzahl = 0;                            
7
   char Zeichen;
8
   char *Inhalt;
9
  
10
   FILE *tf;
11
   tf = fopen("test.txt","rb");
12
   if(tf==NULL) {
13
                 pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); }
14
                 else{ pspDebugScreenPrintf("Laden der Datei war erfolgreich\n");}
15
                
16
   while(!feof(tf))
17
   //while(Zeichen!=EOF)
18
   {
19
   Zeichen = fgetc(tf);
20
  
21
   if(Zeichen!='\n')
22
        {
23
        Zeichenanzahl++;
24
        Inhalt = (char *) malloc(Zeichenanzahl);
25
        strcat(Inhalt,Zeichen);
26
        }          
27
   }
28
  
29
   fclose(tf);
30
   pspDebugScreenPrintf("Inhalt der Datei lautet:\n\n%s",Inhalt);
31
  
32
   sceKernelSleepThread();
33
   return 0;
34
}

Das gesammte Projekt lässt sich problemlos compilieren. Keine Warnungen 
oder sonstiges. Auf dem Bildschirm wird aber auch keine Ausgabe 
getätigt. Daher vermute ich, das es ein proböem beim Laden der Datei 
gibt. In etlichen vorherigen Projekten gab es aber keine Probleme.

MfG
Gast #2478680
Lesenswert?

>if(tf==NULL) {
>pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); }
Da sollte es dann auch nicht weitergehen, also exit() einfügen oder 
etwas ähnliches, ansonsten knallt es weiter unten.

Auch sehe ich malloc aber kein free, lass das nicht den Karl-Heinz 
sehen!
Gast #2478686
Lesenswert?

Naja, zumindest muss Dein Code ja "Laden [...] erfolgreich" oder "... 
fehlgeschlagen" ausgeben; daran siehst Du ja, ob die Datei erfolgreich 
geöffnet wurde.

Diese zwei Zeilen:
1
Inhalt = (char *) malloc(Zeichenanzahl);
2
strcat(Inhalt,Zeichen);

an der gezeigten Stelle sind natürlich völliger Blödsinn, da nach jedem 
gelesenen Zeichen ein neuer Speicherbereich allokiert wird und das neue 
Zeichen an den Anfang dieses Bereichs kopiert wird.

Ich ahne ungefähr, was Du vorhast; in diesem Falle wäre realloc() 
angebracht.
Effizienter wäre es allerdings, den Speicher nicht byteweise zu 
reallokieren, sondern blockweise (z. B. in 4K-Blöcken).
Gast #2478721
Lesenswert?

So, das Problem liegt wie Ihr alle auch schon sagtet, bei
1
Inhalt = (char *) malloc(Zeichenanzahl);

Wenn ich es so mache, läuft es ohne Probleme. Nur habe ich jetzt eine 
feste Größe definiert. Ich möchte es aber dynamisch haben.

1
char Inhalt[5000]; 
2

3
   ...[...]...
4

5
   while(!feof(tf))
6
   //while(Zeichen!=EOF)
7
   {
8
   Zeichen = fgetc(tf);
9
   
10
   if(Zeichen!='\n')
11
        {
12
        Zeichenanzahl++;
13
        Inhalt[Zeichenanzahl]=Zeichen;
14
        }          
15
   }
16
   
17
   fclose(tf);

Also muss ich mir nur noch einmal anschauen, wie man einen String 
dynamisch vergrößern kann. Habe sowas seit Ebigkeiten nicht mehr 
gemacht.
Danke ann alle.

MfG
#2478747
Lesenswert?

Hallo Freak

Statt realloc() kannst du einfach einen genug grossen Speicher am Anfang 
reservieren.

mit fseek() springs du ans ende der Datei, dann fragst du mit ftell() 
die Position ab, somit weisst du wie gross die Datei ist.

Danach reserviertst du size + 1 mit malloc, das ist auf jeden Fall genug 
Speicher.

Danach kannst du Zeichen für Zeichen einlesen (wenn auch nicht besonders 
performant) und schreibst als letztes ein 0 ('\0') in das Array, und 
somit hast du einen String.

Das du zufiel Speicher reserviert hast ist nicht so schlimm, sofern die 
der Speicher nicht ausgeht, du könntest dann mit strlen() und strncpy() 
das in einen neuen Speicher kopieren, der die exakte Grösse hat.

FILE * fp = fopen(...);
fseek(fp, 0, SEEK_END); // ans ende
long size = ftell(fp);
fseek(fp, 0, SEEK_SET); // wider an den Anfang

char * tmp = malloc(size + 1);

// hier einlesen

int len = strlen(tmp);
char * str = malloc(len + 1);
strncpy(str, tmp, len);

free(tmp); // Speicher freigeben!

Auch str muss mit free freigegeben werden, wenn nicht mehr gebraucht.

Alle angaben ohne Gewähr, nicht getestet...

mfg Andreas
Gast #2478760
Lesenswert?

Freak schrieb:
> Klaus Wachtler schrieb:
>> realloc()
>
> Das ist mein Problem. Bei realloc hängt sie sich jedes mal auf. Ich
> schau mir schon verzweifelt die Referenzen an. Irgendwo muss ich was
> übersehen haben.

realloc ist eigentlich problemlos, man muss nur darauf achten dass keine 
Speicherlecks entstehen wenn die Funktion fehlschlägt:
1
int *ptr;
2

3
ptr=alloc(42);
4
if(ptr==NULL)
5
  exit(1);
6
ptr=realloc(ptr,84); <---- Wenn das fehlschlägt geht die Referenz auf den ersten Speicherblock verloren (wird mit NULL überschrieben) --> Speicherleck.
7
if(ptr==NULL)
8
  exit(2);

Lösung: ptr_tmp und nach dem Test auf 0 wieder ptr zuweisen.
Gast #2478768
Lesenswert?

Ich versteh es langsam nicht mehr.
1
   int Zeichenanzahl = 0;  
2

3
   [...]  
4

5
   while(!feof(tf))
6
   {
7
   Zeichen = fgetc(tf);
8
   
9
   if(Zeichen!='\n')
10
        {
11
        //Zeichenanzahl++;
12
        Zeichenanzahl=Zeichenanzahl+1;
13
        //Inhalt = (char *) malloc(Zeichenanzahl+1); //+1 da Terminierungszeichen
14
        Inhalt = (char *) realloc(Inhalt,Zeichenanzahl+1);
15
        Inhalt[Zeichenanzahl-1]=Zeichen;  //Da Array bei 0 beginnt
16
        }          
17
   }

Ich öffne die Datei und lese das erste Zeichen. Damit wird Zeichenanzahl 
auf 1 erhöht. Anschließend erweitere ich den String auf 2 
(Zeichenanzahl+1), da das Terminierungszeichen am Ende dazukommt. 
Schlussendlich schreibe ich an Position Zeichenanzahl-1 (da Arrays mit 0 
beginnen) das ausgelesene Zeichen.

Aber warum klappt es nicht?
#2478785
Lesenswert?

Freak schrieb:
> Ich versteh es langsam nicht mehr.
>...
> Aber warum klappt es nicht?

WAS heisst "klappt nicht"?

Der Code den du gepostet hat hat keinen Offensichtlichen Fehler din.

Bendenke aber, normalerweise vergrössert realloc den Speicher nicht, 
sondern alloziiert einen neuen Speicherbereich und kopiert alles vom 
alten Speicher in den neuen Speicher, und gibt dann den alten Speicher 
frei.

Das ist extrem langsam. (Komplexität O(n^2) in etwa, falls dir das was 
sagt)

Wenn dann machst du ein realloc alle 1024 Zeichen oder so, oder eben 
ftell.


mfg Andreas
#2478797
Lesenswert?

Klaus Wachtler schrieb:
> Andreas B. schrieb:
>> Das ist extrem langsam. (Komplexität O(n^2) in etwa, falls dir das was
>> sagt)
>
> evtl. langsam ja, aber wieso O(n^2)? Bei einem realloc(): nein oder bei
> allen zusammengenommen: notfalls ja?

Ja sorry, hättes besser weggelassen.

Ich meinte alles Zusammen O(n^2) weil realloc oftmals kopieren muss.

Wird statt realloc am Anfang genug Speicher reserviert läuft das ganze 
mit O(n).

Beweisen kann ich die Behauptung nicht, da ich mich nicht mit der PSP 
auskenne...

Aber wahrscheinlich ist es schneller ohne realloc...



mfg Andreas
Gast #2478844
Lesenswert?

Klaus Wachtler schrieb:
> Oder was in den Programmteilen steht, die keiner sieht?

Da steht folgendes. Nichts relevantes für das Problem.
1
int main() {
2
    
3
   SetupCallbacks();
4
   pspDebugScreenInit();
5
   
6
   int Zeichenanzahl = 0;                            
7
   char Zeichen;
8
   char *Inhalt;
9
   //char Inhalt[5000]; 
10
                 
11
   FILE *tf;
12
   tf = fopen("test.txt","rb");
13
   if(tf==NULL) { 
14
                 pspDebugScreenPrintf("Laden der Datei fehlgeschlagen\n"); 
15
                 sceKernelExitGame();
16
                 }
17
                 else{ pspDebugScreenPrintf("Laden der Datei war erfolgreich\n");}

Andreas B. schrieb:
> Der Code den du gepostet hat hat keinen Offensichtlichen Fehler din.
So sehe ich das auch und trotzdem stimmt was nicht. Ich gehe aber nun 
davon aus, dass es systembedingt ist.

Nun bleibt noch die Möglichkeit - wie auch schon gesagt wurde - vorher 
die Dateigröße/Zeichenanzahl zu bestimmen. Nur wollte ich mir die 
Rechenzeit dafür sparen. Wenn es aber nicht anders geht, dann eben so. 
Ich danke allen die mir bei der Problembeseitigung geholfen haben.

Einen guten Rutsch. :)
MfG
Gast #2478857
Lesenswert?

Freak schrieb:
> Nur wollte ich mir die
> Rechenzeit dafür sparen. Wenn es aber nicht anders geht, dann eben so.
> Ich danke allen die mir bei der Problembeseitigung geholfen haben.

Und du meinst, das ständige realloc mit umkopieren ist schneller?

Und realloc muss beim ersten Aufruf einen NULL-Zeiger bekommen.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren