Forum: PC-Programmierung Daten aus Datei einlesen und zeilenweise abespeichern


von Eric (Gast)


Lesenswert?

moin,

lese eine Datei zeilenweise aus. jetzt will ich in einem array jeder 
zeile gespeichert haben, z.B das ist dann über einen index i zeile[i] 
den kompletten string angezeigt bekomme, weiss jemand en tipp. auslesen 
der datei geht schon.

hab also ne while-Schleife(feof) {  fgets(string,....)}; will dann 
sozusagen  irgendwie schreiben zeile[i]= string

danke schon ma

von Karl H. (kbuchegg)


Lesenswert?

Eric schrieb:
> moin,
>
> lese eine Datei zeilenweise aus. jetzt will ich in einem array jeder
> zeile gespeichert haben, z.B das ist dann über einen index i zeile[i]
> den kompletten string angezeigt bekomme, weiss jemand en tipp. auslesen
> der datei geht schon.
>
> hab also ne while-Schleife(feof)

schon falsch.
C schaut nicht in die Zukunft. feof dient dazu, nachdem dir die 
Lesefunktion angezeigt hat, dass sie nicht mehr lesen konnte, 
festzustellen ob das 'Nicht-lesen-können' daraus resultierte, dass die 
Datei zu ende ist

 {  fgets(string,....)}; will dann
> sozusagen  irgendwie schreiben zeile[i]= string

Strings kann man so nicht umkopieren.

http://www.mikrocontroller.net/articles/FAQ#Wie_funktioniert_String-Verarbeitung_in_C.3F

Wie gross hast du zeile als 2-dimensionales Array dimensioniert?
1
   char zeile[512][81];  // Platz für 512 Zeilen zu je 80 Zeichen.
2
3
4
   i = 0;
5
   while( fgets( string, ... ) ) {
6
     strcpy( zeile[i++], string );
7
   }
8
9
   if( !feof( ... ) )
10
     printf( "Fehler beim Lesen der Datei\n" );

Wobei sich in diesem Fall allerdings die Frage erhebt, warum du da den 
Umweg über eine Hilfsvariable string machst.

von P. S. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:

> Wie gross hast du zeile als 2-dimensionales Array dimensioniert?
>
>
1
>      strcpy( zeile[i++], string );
2
>

strncpy, bitte. Besser waere natuerlich noch strdup und nur ein 
Pointerarray.

von Karl H. (kbuchegg)


Lesenswert?

Peter Stegemann schrieb:
> Karl heinz Buchegger schrieb:
>
>> Wie gross hast du zeile als 2-dimensionales Array dimensioniert?
>>
>>
1
>>      strcpy( zeile[i++], string );
2
>>
>
> strncpy, bitte. Besser waere natuerlich noch strdup und nur ein
> Pointerarray.

Besser wär eine dynamisch aufgebaute Datenstruktur (Liste von Strings).
Es ist immer unklug, sich auf eine Maximallänge einer Datei zu 
verlassen.

strncpy hilft dir in dem Fall auch nichts, weil schon 'string' eine 
maximale Länge vorgibt, die nach dem fgets richtig behandelt werden 
will.

von P. S. (Gast)


Lesenswert?

Karl heinz Buchegger schrieb:
> Peter Stegemann schrieb:

>> strncpy, bitte. Besser waere natuerlich noch strdup und nur ein
>> Pointerarray.
> Besser wär eine dynamisch aufgebaute Datenstruktur (Liste von Strings).
> Es ist immer unklug, sich auf eine Maximallänge einer Datei zu
> verlassen.

"Besser" liesse sich hier sicherlich noch ein paar mal steigern :-)

> strncpy hilft dir in dem Fall auch nichts, weil schon 'string' eine
> maximale Länge vorgibt, die nach dem fgets richtig behandelt werden
> will.

Ich sehe hier keine deklaration von "string" und selbst wenn sie da 
waere, ich wuerde mich nie darauf verlassen, dass die Quellgroesse 
kleinergleich der Zielgroesse ist. Sowas aendert sich auch schnell 
mal...

von Eric (Gast)


Lesenswert?

danke erst mal;

hab auch noch das Problem wie gesagt, das die Datei nicht immer gleich 
groß. Nur sind meine c Kenntnisse auch nicht so fundamental, und bei 
meinen pdf "c in 21 tagen" steht bei dateien ja das man für die zeile so 
wie die länge immer schon vorher speicher reservieren muss, mich würde 
interessieren, wie ich das, wie oben geschrieben, dynamisch machen kann.

von Klaus W. (mfgkw)


Lesenswert?

In C ist das sehr mühsam, wenn man es effizient und wasserdicht haben 
will.

Wenn du dich mit C++ anfreunden willst, geht es wesentlich einfacher:
1
#include <iostream>
2
#include <fstream>
3
#include <string>
4
#include <list>
5
6
7
int main( int nargs, char **args )
8
{
9
  std::string zeile;
10
  std::ifstream   datei( "t.cpp" );
11
  std::list< std::string >  liste;
12
13
  // Zeilenweise einlesen, dabei wird der String zeile intern
14
  // angepasst:
15
  while( getline( datei, zeile ) )
16
  {
17
    // An Liste anfügen (wird automatisch verlängert)
18
    liste.push_back( zeile );
19
  }
20
21
  // Ausgabe zur Kontrolle:
22
  for( std::list< std::string >::const_iterator it=liste.begin();
23
       it!=liste.end();
24
       ++it
25
       )
26
  {
27
    std::cout << "gelesene Zeile: " << (*it) << std::endl;
28
  }
29
}

von P. S. (Gast)


Lesenswert?

Wasserdicht? Was macht dein Programm, wenn das File nicht existiert?

von Klaus W. (mfgkw)


Lesenswert?

1.bezog sich die Ftrage und damit auch die Antwort darauf, ohne
Pufferüberlauf Zeilen zu lesen, deren Länge man nicht vorher kennt.
2. Ist in dem von dir beschriebenen Fall halt die Liste leer, weil
bereits beim ersten Durchlauf der cast con std::istream nach bool
einfach false liefert.

von Karl H. (kbuchegg)


Lesenswert?

Peter Stegemann schrieb:
> Wasserdicht? Was macht dein Programm, wenn das File nicht existiert?

OK. Eine Meldung wär schön.
Aber abgesehen davon passiert nichts weiter Schlimmes.

von Karl H. (kbuchegg)


Lesenswert?

> In C ist das sehr mühsam, wenn man es effizient und wasserdicht
> haben will.

Du kannst das 'effizient' ruhig weglassen :-)
Wasserdicht als Grundforderung alleine würde schon reichen.

von Klaus W. (mfgkw)


Lesenswert?

naja, zu langsam soll es ja auch nicht werden.
Man könnte z.B. zeichenweise lesen und mit jedem Zeichen den Puffer
mit realloc() um eins verlängern.
So etwas würde ich als ineffizient bezeichnen und gleich ausschließen,
daher die Formulierung.

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> naja, zu langsam soll es ja auch nicht werden.
> Man könnte z.B. zeichenweise lesen und mit jedem Zeichen den Puffer
> mit realloc() um eins verlängern.

OK. mach aus der 1 eine 10 oder 20 und dann sieht diese Strategie gar 
nicht mehr so schlecht aus. Das ist noch relativ simpel zu machen und 
ist im Vergleich zu dem was sonst da noch so alles abläuft gar nicht 
mehr so schlecht.

Aber darauf wollte ich eigentlich gar nicht so sehr hinaus. Mir wäre 
schon lieb, wenn derartige Eingabesyssteme in C wenigstens wasserdicht 
wären.

Sowas ist immer eine herrliche Diskussion in den comp.lang Foren. Da 
wird hemmungslos C mit C++ Code verglichen und dass C++ dann sooooo 
langsam wäre. Nur wenn man dann mal die Codes vergleicht kommt man 
drauf, dass wieder mal Äpfel mit Birnen verglichen werden. Während C++ 
Code Bufferoverflows aus dem Stand heraus vermeidet, ist der angeblich 
funktional gleichwertige C Code (der liest ja auch nur eine Datei ein) 
hemmungslos anfällig für alle möglichen Overflows.

Fazit: Wenn es nicht korrekt sein muss, ist es leicht schnell zu sein 
:-)

von Eric (Gast)


Lesenswert?

muss es aber in C programmieren, weil ich das dann in MatLab über 
mex-file einbinden muss. Geht zwar auch in Matlab die datei einzulesen, 
aber da brauchte ich 28s und in C nur 2s. C muss sein weil matlab als 
standard nur einen c compiler hat. es werden zwar bestimmte c++ compiler 
unterstützt aber die Kosten geld (borland c++). wäre wirklich nett, wenn 
wie oben c++ code auch einer ein  primitives c beispiel posten könnte

von P. S. (Gast)


Lesenswert?

Eric schrieb:

> muss es aber in C programmieren, weil ich das dann in MatLab über
> mex-file einbinden muss. Geht zwar auch in Matlab die datei einzulesen,
> aber da brauchte ich 28s und in C nur 2s. C muss sein weil matlab als
> standard nur einen c compiler hat. es werden zwar bestimmte c++ compiler
> unterstützt aber die Kosten geld (borland c++). wäre wirklich nett, wenn
> wie oben c++ code auch einer ein  primitives c beispiel posten könnte

Wenn du es genauso dynamisch haben willst, musst du dir eine kleine 
Liste schreiben. Wenn du es nicht kannst, wird es Zeit, das zu lernen. 
Nein, ich schreibe dir jetzt keine, meine Zeit waechst auch nicht auf 
Baeumen.

Alternativ kannst du dich mit einem Array von Strings durchschlagen, das 
du mit realloc() vergroesserst. Nicht schoen, aber geht auch.

von Klaus W. (mfgkw)


Lesenswert?

auch wenn du meinst, daß es nicht schön ist mit einem Feld, stelle
ich doch mal rein, was ich gestern dazu zusammengebaut habe:
1
/* Time-stamp: "04.06.09 20:36 lese.c klaus<bei>wachtler.de"
2
 *
3
 */
4
5
#include <stdlib.h>
6
#include <stddef.h>
7
#include <stdio.h>
8
#include <string.h>
9
10
11
/* liest eine Zeile aus f, allokiert reichlich Speicher dafür und
12
 * liefert einen Zeiger auf die nullterminierte Zeile.
13
 * Der Aufrufer ist dafür verantwortlich, die Zeile irgendwann wieder
14
 * freizugeben!
15
 *
16
 * Der Zeilenvorschub wird unterdrückt und nicht zurückgegeben.
17
 *
18
 * Ist die Zeile leer (außer dem LF), enthält der gelieferte String
19
 * nur die abschließende '\0'.
20
 *
21
 * Im Fehlerfall (Dateiende schon beim ersten gelesenen Zeichen, kein
22
 * Speicher) wird NULL zurückgegeben.
23
 */
24
char *leseZeileAlloc( FILE *f )
25
{
26
  int      zeichen;
27
  char    *zeile       = NULL;  /* gelesene Zeile, anfangs
28
                                 * ohne '\0' */
29
  size_t   lZeile      = 0;     /* tatsächliche Länge+1 */
30
  size_t   lZeileAlloc = 0;     /* allokierte Länge */
31
32
  /* es wird Zeichen für Zeichen gelesen, bei Bedarf der allokierte
33
   * Speicher verlängert.
34
   * Die allokierte Länge ist immer um mindestens 1 größer als bisher
35
   * nötig, um zum Schluß noch die terminierende 0 anfügen zu können.
36
   */
37
38
  while( (zeichen=fgetc( f ))!=EOF )
39
  {
40
    /* Zeichen gelesen, also ablegen: */
41
42
    if( lZeile+1>=lZeileAlloc )
43
    {
44
      /* Zeile muss verlängert werden! */
45
      char *neueZeile = NULL;
46
      if( lZeileAlloc==0 )
47
      {
48
        lZeileAlloc = 10; /* Anfangswert */
49
      }
50
      else
51
      {
52
        lZeileAlloc *= 2; /* ansonsten immer verdoppeln */
53
      }
54
      neueZeile = (char*)malloc( lZeileAlloc );
55
      if( !neueZeile )
56
      {
57
        /* Allokieren gescheitert */
58
        free( zeile );
59
        return NULL;
60
      }
61
      if( lZeile )
62
      {
63
        memcpy( neueZeile, zeile, lZeile );
64
      }
65
      free( zeile );
66
      zeile = neueZeile;
67
      neueZeile = NULL;
68
    }
69
    if( zeichen!='\n' )
70
    {
71
      zeile[lZeile++] = (char)zeichen;
72
    }
73
74
    if( zeichen=='\n' )
75
    {
76
      /* Abbruch bei LF */
77
      break;
78
    }
79
  }
80
81
  /* terminieren */
82
  if( zeile )
83
  {
84
    zeile[lZeile] = '\0';
85
  }
86
87
  return zeile;
88
}
89
90
91
/* liest alle Zeilen aus f und speichert die dafür allokierten Zeilen
92
 * in einem Feld von char* (welches dafür allokiert wird).
93
 *
94
 * Dieses Feld wird an der Stelle *pZeilen hinterlegt und muß ebenso
95
 * wie der Speicher, auf den die darin abgelegten Zeiger verweisen,
96
 * vom Aufrufer wieder freigegeben werden.
97
 *
98
 * Rückgabe ist die Anzahl der erfolgreich gelesenen Zeilen.
99
 */
100
size_t  leseDateiZeilenweiseAlloc( FILE *f, char ***pZeilen )
101
{
102
  char    **zeilen = NULL;
103
  size_t    nZeilen = 0;
104
  char     *zeile;
105
106
  while( ( zeile = leseZeileAlloc( f ) ) )
107
  {
108
    /* Feld um eine Zeile verlängern */
109
110
    char   **neueZeilen = (char**)malloc( (nZeilen+1)*sizeof(*neueZeilen) );
111
    if( !neueZeilen )
112
    {
113
      /* Speicherfehler!
114
       * Alles wieder freigeben und abbrechen
115
       */
116
      size_t    iZeile;
117
      for( iZeile=0; iZeile<nZeilen; ++iZeile )
118
      {
119
        free( zeilen[iZeile] ); zeilen[iZeile] = NULL;
120
      }
121
      free( zeilen ); zeilen = NULL;
122
      *pZeilen = NULL;
123
      return 0;
124
    }
125
    if( nZeilen )
126
    {
127
      memcpy( neueZeilen, zeilen, nZeilen*sizeof(*zeilen) );
128
    }
129
    zeilen = neueZeilen; neueZeilen = NULL;
130
131
    /* gelesene Zeile einhängen */
132
    zeilen[nZeilen++] = zeile;
133
  }
134
135
  *pZeilen = zeilen;
136
  return nZeilen;
137
}
138
139
140
int main( int nargs, char **args )
141
{
142
  char   **zeilen = NULL;
143
  size_t   nZeilen = 0;
144
  FILE    *f = fopen( "lese.c", "r" );
145
146
  size_t iZeile;
147
148
  if( f )
149
  {
150
    /* Datei lesen:
151
     */
152
    nZeilen = leseDateiZeilenweiseAlloc( f, &zeilen );
153
    fclose( f ); f = NULL;
154
155
156
    /* Inhalt anzeigen:
157
     */
158
    for( iZeile=0; iZeile<nZeilen; ++iZeile )
159
    {
160
      printf( "gelesene Zeile %3lu: <%s>\n",
161
              (unsigned long)iZeile,
162
              zeilen[iZeile]
163
              );
164
    }
165
166
    /* zum Schluß alles wieder freigeben */
167
    for( iZeile=0; iZeile<nZeilen; ++iZeile )
168
    {
169
      free( zeilen[iZeile] ); zeilen[iZeile] = NULL;
170
    }
171
    free( zeilen ); zeilen = NULL;
172
173
  }
174
  else
175
  {
176
    fprintf( stderr, "Kann die Datei nicht öffnen!\n" );
177
  }
178
179
  return 0;
180
}

So in etwa könnte es in C aussehen, wenn man alles dynamisch haben will.

Verbessern könnte man insbesondere, daß in leseDateiZeilenweiseAlloc()
das Feld immer nur um 1 verlängert wird; effektiver wäre es ähnlich
wie in leseZeileAlloc() immer gleich etwas mehr zu nehmen, dafür dann
seltener verlängern zu müssen. Kann aber jeder selber machen...

Weiterhin habe ich nicht realloc() genommen, sondern hole mir mit
malloc() Speicher, kopiere mit memcpy und gebe den alten dann frei.
Grund: Ich glaube mich zu entsinnen, daß man in Matlab die malloc()
und free() durch Matlab-Funktionen ersetzen muss (bin mir da aber
nicht sicher), und ich nicht weiß, ob es da ein realloc() überhaupt
gibt. So kann man es im Zweifelsfall jedenfalls leichter umbauen.

Von der Funktionalität her entspricht es i.W. dem C++-Beispiel von oben,
leider etwas umständlicher.

Achtung!
Getestet ist nur soweit, daß es einmal eine Datei gelesen hat.
Vor allem die gesamte Fehlerbehandlung ist nie ausprobiert worden.

von P. S. (Gast)


Lesenswert?

Klaus Wachtler schrieb:

> Verbessern könnte man insbesondere, daß in leseDateiZeilenweiseAlloc()
> das Feld immer nur um 1 verlängert wird; effektiver wäre es ähnlich
> wie in leseZeileAlloc() immer gleich etwas mehr zu nehmen, dafür dann
> seltener verlängern zu müssen. Kann aber jeder selber machen...

Ich wuerde die Groesse nicht verdoppeln. Eher z.B. um 10% vergroessern.

> Weiterhin habe ich nicht realloc() genommen, sondern hole mir mit
> malloc() Speicher, kopiere mit memcpy und gebe den alten dann frei.
> Grund: Ich glaube mich zu entsinnen, daß man in Matlab die malloc()
> und free() durch Matlab-Funktionen ersetzen muss (bin mir da aber
> nicht sicher), und ich nicht weiß, ob es da ein realloc() überhaupt
> gibt. So kann man es im Zweifelsfall jedenfalls leichter umbauen.

Ich wuerde trotzdem realloc() nehmen und im Zweifelsfall dann eben 
realloc ueber malloc() und memcpy() implementieren. Ist praktisch 
genauso einfach, im Fall der Existenz von realloc() aber wesentlich 
effizienter.

von Klaus W. (mfgkw)


Lesenswert?

Peter Stegemann schrieb:
> Klaus Wachtler schrieb:
>
>> Verbessern könnte man insbesondere, daß in leseDateiZeilenweiseAlloc()
>> das Feld immer nur um 1 verlängert wird; effektiver wäre es ähnlich
>> wie in leseZeileAlloc() immer gleich etwas mehr zu nehmen, dafür dann
>> seltener verlängern zu müssen. Kann aber jeder selber machen...
>
> Ich wuerde die Groesse nicht verdoppeln. Eher z.B. um 10% vergroessern.

Naja, war nur als Vorschlag gedacht wie man es machen könnte.

Wenn man es vernünftig optimieren will, muß man ohnehin überlegen,
was malloc() intern macht.
Vermutlich wird es minimale Granularität geben, in der intern Blöcke
allokiert werden, und vermutlich wird auch nicht jedes beliebige
Vielfache davon allokiert werden.
Ich hatte mal eine einfache Speicherverwaltung gebaut und kam dabei
zu dem Ergebnis, daß es zu wesentlicher weniger Fragmentierung
führt, nur eine Mindestblockgröße und 2^n-fache davon wirklich
zu reservieren. Alle anderen angeforderten Größen werden einfach
entsprechend aufgerundet.
Ich denke, ähnlich wird das malloc() auch arbeiten.

Falls das so ist, ist weder mein Vorschlag noch deiner ideal,
sondern es wäre vielmehr am besten, genau solche Größen
zu belegen.


>
>> Weiterhin habe ich nicht realloc() genommen, sondern hole mir mit
>> malloc() Speicher, kopiere mit memcpy und gebe den alten dann frei.
>> Grund: Ich glaube mich zu entsinnen, daß man in Matlab die malloc()
>> und free() durch Matlab-Funktionen ersetzen muss (bin mir da aber
>> nicht sicher), und ich nicht weiß, ob es da ein realloc() überhaupt
>> gibt. So kann man es im Zweifelsfall jedenfalls leichter umbauen.
>
> Ich wuerde trotzdem realloc() nehmen und im Zweifelsfall dann eben
> realloc ueber malloc() und memcpy() implementieren. Ist praktisch
> genauso einfach, im Fall der Existenz von realloc() aber wesentlich
> effizienter.

ICH würde es auch so machen, aber der OP liest sich für mich eher,
als ob eine solche Änderung nicht unbedingt problemlos wäre.
Deshalb dachte ich es wäre ein netter Service, soweit voraus zu
denken.
Meinen Vorschlag mit realloc() zu vereinfachen, ist wohl einfacher
als andersrum.
Aber egal, wie man es macht, macht man es halt falsch ;-)
Hängt halt davon ab, wie es nun in Matlab ist. Aber das
wollte ich nicht herausfinden, dazu war ich zu faul.

von Karl H. (kbuchegg)


Lesenswert?

Klaus Wachtler schrieb:
> Peter Stegemann schrieb:
>> Klaus Wachtler schrieb:
>>
>>> Verbessern könnte man insbesondere, daß in leseDateiZeilenweiseAlloc()
>>> das Feld immer nur um 1 verlängert wird; effektiver wäre es ähnlich
>>> wie in leseZeileAlloc() immer gleich etwas mehr zu nehmen, dafür dann
>>> seltener verlängern zu müssen. Kann aber jeder selber machen...
>>
>> Ich wuerde die Groesse nicht verdoppeln. Eher z.B. um 10% vergroessern.
>
> Naja, war nur als Vorschlag gedacht wie man es machen könnte.
>
> Wenn man es vernünftig optimieren will, muß man ohnehin überlegen,
> was malloc() intern macht.

Nicht nur das.
Ich würd mir auch mal typische Files ansehen (so das möglich ist).
Wenn sich da rausstellt, dass so über den Daumen die durchschnittliche 
Zeilenlänge im File um die 60 Zeichen ist, dann würd ich das als 
Default-Erst-Allokierung nehmen und erst ab dort vergrößern.

> Ich hatte mal eine einfache Speicherverwaltung gebaut und kam dabei
> zu dem Ergebnis, daß es zu wesentlicher weniger Fragmentierung
> führt, nur eine Mindestblockgröße und 2^n-fache davon wirklich
> zu reservieren.

Kann mich noch erinnern, dass mir im Studium erzählt wurde, dass eine 
gute Strategie auch darin besteht, die Fibonacci Folge als Blockgrößen 
zu benutzen. Die Begründung weiß ich nicht mehr, aber das hab ich mir 
gemerkt, weil es so ziemlich das einzige mal war, dass diese Folge für 
irgendetwas gut war.

Aber wie heißt es so schön:
Machs erst korrekt, und erst dann machs schnell.

Optimization is the root of all evil
Optimization: Rule 1: Don't do it
              Rule 2: Don't do it ... yet

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.