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
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.
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.
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.
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...
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.
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 | }
|
Wasserdicht? Was macht dein Programm, wenn das File nicht existiert?
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.
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.
> 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.
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.
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 :-)
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
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.
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.
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.
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.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.