mikrocontroller.net

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


Autor: Eric (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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_fu...

Wie gross hast du zeile als 2-dimensionales Array dimensioniert?
   char zeile[512][81];  // Platz für 512 Zeilen zu je 80 Zeichen.


   i = 0;
   while( fgets( string, ... ) ) {
     strcpy( zeile[i++], string );
   }

   if( !feof( ... ) )
     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.

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl heinz Buchegger schrieb:

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

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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Stegemann schrieb:
> Karl heinz Buchegger schrieb:
>
>> Wie gross hast du zeile als 2-dimensionales Array dimensioniert?
>>
>>
>>      strcpy( zeile[i++], string );
>> 
>
> 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.

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Eric (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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:
#include <iostream>
#include <fstream>
#include <string>
#include <list>


int main( int nargs, char **args )
{
  std::string zeile;
  std::ifstream   datei( "t.cpp" );
  std::list< std::string >  liste;

  // Zeilenweise einlesen, dabei wird der String zeile intern
  // angepasst:
  while( getline( datei, zeile ) )
  {
    // An Liste anfügen (wird automatisch verlängert)
    liste.push_back( zeile );
  }

  // Ausgabe zur Kontrolle:
  for( std::list< std::string >::const_iterator it=liste.begin();
       it!=liste.end();
       ++it
       )
  {
    std::cout << "gelesene Zeile: " << (*it) << std::endl;
  }
}

Autor: P. S. (Gast)
Datum:

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

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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 
:-)

Autor: Eric (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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:
/* Time-stamp: "04.06.09 20:36 lese.c klaus<bei>wachtler.de"
 *
 */

#include <stdlib.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>


/* liest eine Zeile aus f, allokiert reichlich Speicher dafür und
 * liefert einen Zeiger auf die nullterminierte Zeile.
 * Der Aufrufer ist dafür verantwortlich, die Zeile irgendwann wieder
 * freizugeben!
 *
 * Der Zeilenvorschub wird unterdrückt und nicht zurückgegeben.
 *
 * Ist die Zeile leer (außer dem LF), enthält der gelieferte String
 * nur die abschließende '\0'.
 *
 * Im Fehlerfall (Dateiende schon beim ersten gelesenen Zeichen, kein
 * Speicher) wird NULL zurückgegeben.
 */
char *leseZeileAlloc( FILE *f )
{
  int      zeichen;
  char    *zeile       = NULL;  /* gelesene Zeile, anfangs
                                 * ohne '\0' */
  size_t   lZeile      = 0;     /* tatsächliche Länge+1 */
  size_t   lZeileAlloc = 0;     /* allokierte Länge */

  /* es wird Zeichen für Zeichen gelesen, bei Bedarf der allokierte
   * Speicher verlängert.
   * Die allokierte Länge ist immer um mindestens 1 größer als bisher
   * nötig, um zum Schluß noch die terminierende 0 anfügen zu können.
   */

  while( (zeichen=fgetc( f ))!=EOF )
  {
    /* Zeichen gelesen, also ablegen: */

    if( lZeile+1>=lZeileAlloc )
    {
      /* Zeile muss verlängert werden! */
      char *neueZeile = NULL;
      if( lZeileAlloc==0 )
      {
        lZeileAlloc = 10; /* Anfangswert */
      }
      else
      {
        lZeileAlloc *= 2; /* ansonsten immer verdoppeln */
      }
      neueZeile = (char*)malloc( lZeileAlloc );
      if( !neueZeile )
      {
        /* Allokieren gescheitert */
        free( zeile );
        return NULL;
      }
      if( lZeile )
      {
        memcpy( neueZeile, zeile, lZeile );
      }
      free( zeile );
      zeile = neueZeile;
      neueZeile = NULL;
    }
    if( zeichen!='\n' )
    {
      zeile[lZeile++] = (char)zeichen;
    }

    if( zeichen=='\n' )
    {
      /* Abbruch bei LF */
      break;
    }
  }

  /* terminieren */
  if( zeile )
  {
    zeile[lZeile] = '\0';
  }

  return zeile;
}


/* liest alle Zeilen aus f und speichert die dafür allokierten Zeilen
 * in einem Feld von char* (welches dafür allokiert wird).
 *
 * Dieses Feld wird an der Stelle *pZeilen hinterlegt und muß ebenso
 * wie der Speicher, auf den die darin abgelegten Zeiger verweisen,
 * vom Aufrufer wieder freigegeben werden.
 *
 * Rückgabe ist die Anzahl der erfolgreich gelesenen Zeilen.
 */
size_t  leseDateiZeilenweiseAlloc( FILE *f, char ***pZeilen )
{
  char    **zeilen = NULL;
  size_t    nZeilen = 0;
  char     *zeile;

  while( ( zeile = leseZeileAlloc( f ) ) )
  {
    /* Feld um eine Zeile verlängern */

    char   **neueZeilen = (char**)malloc( (nZeilen+1)*sizeof(*neueZeilen) );
    if( !neueZeilen )
    {
      /* Speicherfehler!
       * Alles wieder freigeben und abbrechen
       */
      size_t    iZeile;
      for( iZeile=0; iZeile<nZeilen; ++iZeile )
      {
        free( zeilen[iZeile] ); zeilen[iZeile] = NULL;
      }
      free( zeilen ); zeilen = NULL;
      *pZeilen = NULL;
      return 0;
    }
    if( nZeilen )
    {
      memcpy( neueZeilen, zeilen, nZeilen*sizeof(*zeilen) );
    }
    zeilen = neueZeilen; neueZeilen = NULL;

    /* gelesene Zeile einhängen */
    zeilen[nZeilen++] = zeile;
  }

  *pZeilen = zeilen;
  return nZeilen;
}


int main( int nargs, char **args )
{
  char   **zeilen = NULL;
  size_t   nZeilen = 0;
  FILE    *f = fopen( "lese.c", "r" );

  size_t iZeile;

  if( f )
  {
    /* Datei lesen:
     */
    nZeilen = leseDateiZeilenweiseAlloc( f, &zeilen );
    fclose( f ); f = NULL;


    /* Inhalt anzeigen:
     */
    for( iZeile=0; iZeile<nZeilen; ++iZeile )
    {
      printf( "gelesene Zeile %3lu: <%s>\n",
              (unsigned long)iZeile,
              zeilen[iZeile]
              );
    }

    /* zum Schluß alles wieder freigeben */
    for( iZeile=0; iZeile<nZeilen; ++iZeile )
    {
      free( zeilen[iZeile] ); zeilen[iZeile] = NULL;
    }
    free( zeilen ); zeilen = NULL;

  }
  else
  {
    fprintf( stderr, "Kann die Datei nicht öffnen!\n" );
  }

  return 0;
}

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.

Autor: P. S. (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Klaus Wachtler (mfgkw)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.