Textdatei: bestimmte Zeile ausgeben
-
Hallo Leute
arbeite gerade etwas mit C und C++. Ziel ist auf eine Textdatei zuzugreifen und dort eine bestimmte Zeile ausgeben zu lassen.
1. Die Textdatei hat unterschiedlihe Größen ( einmal 200 Zeilen, einmal 350 usw.)
2. Es muss IMMER die letzte Zeile sein, auf die ich zugreife bzw. die ausgegeben werden mussMein Ansatz lautet folgendermaßen:
myStream = fopen("Z:\\blblabla.txt", "r"); //Falls File korrekt geöffnet wurde if(myStream!=NULL) { printf("erfolgreich, ihr Freaks! \n\n\n"); while((temp = fgetc(myStream))!=EOF) { if ((char) temp == '\n') zeilenzahl = zeilenzahl +1; } printf("Anzahl der Zeilen: %d ", zeilenzahl); fclose(myStream); } else printf("File kann nicht geoeffnet werden\n"); printf("\n\n OKAY !!! \n\n"); system("pause"); return 0; }
Das heißt mein Programm zählt alle Zeilen und gibt mir dann quasi aus, wie viele Zeilen es hat und gibt es mir aus. Wie schaffe ich es jetzt, dass Programm so zu schreiben, dass er mir genau die letzte Zeile abspeichert und ausgiebt ? Ansatz war es, bis zu dieseer Zeilenanzahl in ner Schleife hochzuzählen und das ganze iwie in ein Array abspeichern, aber da weis ich nicht genau wie ich anfangen muss ^^
Lohnt es sich überhaupt das in C zu machen oder sollte man hier eher auf C++ umsteigen ? Also ich komme mit C besser zurecht als mit C++, deswegen hab ich es in C geschrieben!
Ich hoffe ihr könnt mir helfen
MFG
Madleine
-
Das wäre in C++ bedeutend einfacher, da man sich nicht mit Speicherverwaltung rumschlagen muss. Ganz simpel:
#include <fstream> #include <iostream> #include <string> int main() { std::ifstream in("foo.txt"); std::string line; while(std::getline(in, line)); std::cout << line << std::endl; }
Wobei es allerdings eine Sache zu beachten gibt: die letzte Zeile der Datei ist in dieser Interpretation das, was nach dem letzten \n-Zeichen kommt. Wenn man erwartet, dass eine Datei mit einem Newline-Zeichen endet und man in diesem Fall die Zeile vor dem Newline-Zeichen haben will, wird es ein wenig komplexer:
#include <fstream> #include <iostream> #include <string> int main() { std::ifstream in("foo.txt"); std::string line, prev_line; while(std::getline(in, line)) { prev_line = line; } std::cout << (line.empty() ? prev_line : line) << std::endl; }
In C ist es insofern etwas komplexer, als dass man vorher nicht weiß, wie lang die letzte Zeile ist und sich darum kümmern muss. Wie es das Glück will, habe ich etwas derartiges schon rumliegen:
#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> char *get_line(FILE *stream) { size_t const CHUNKSIZE = 128; size_t n = 0; char *buf = NULL, *pos = NULL; if(feof(stream)) return NULL; do { char *p; n += CHUNKSIZE; p = realloc(buf, n + 1); if(p == NULL) { free(buf); return NULL; } buf = p; pos = buf + n - CHUNKSIZE; if(fgets(pos, CHUNKSIZE + 1, stream) == NULL) { if(feof(stream)) { return buf; } else { free(buf); return NULL; } } } while(pos[strlen(pos) - 1] != '\n'); return buf; } int main(void) { FILE *fd = fopen("blabla.txt", "r"); char *line = NULL, *prev_line = NULL; while(line = get_line(fd)) { free(prev_line); prev_line = line; } puts(*line ? line : prev_line); free(prev_line); free(line); return 0; }
-
Okay, also wenn ich deinen Code so einfüg und an meinen Variablen anpasse, dann funktioniert das so nicht !
Muss ich noch auf iwas achten ?
-
Genauere Fehlerbeschreibung wäre sinnvoll (welchen Code du meinst etc.). Ich nehme an, es geht um den C-Code - bei genauerer Betrachtung habe ich das unzureichend hingekladdet. main muss etwa so aussehen:
int main(void) { FILE *fd = fopen("blabla.txt", "r"); char *line = NULL, *pline = NULL, *ppline = NULL; while(line = get_line(fd)) { free(ppline); ppline = pline; pline = line; } if(pline && *pline != '\n') { printf("%s", pline); } else if(ppline) { printf("%s", ppline); } free(ppline); free(pline); return 0; }
Rest unverändert.
-
Okay, hab deine Funktionen beides mal getestet
der Debugger erkennt keinen Fehler aber das Programm stürzt jedes mal ab wenn er die Datei lesen muss !!Die Datei ist ziemlich groß und enthält Ziffern sowie leerzeichen sowie Buchstaben!
Ziel ist es, wie gesagt, nur die letzte Zeile in ein Array zu speichern und dann auszugeben
aber iwie wird das nicht so wie ich es mir vorgestellt habe
-
Kann mir keiner helfen ? Sitze schon den ganzen Tag davor und ich komme hier definitiv nicht weiter. Es geht mir nur darum diese eine letzte Zeile auszugeben, aber das wird absolut nichts.
Ich habe keinen Plan wie ich dem Programm sagen soll, dass er den einzelnen Zeilen Zahlen gibt bzw. sie benennt oder keine ahnungMFG
Madleine
-
#include <fstream> #include <iostream> #include <string> using namespace std; int main() { ifstream in("dat.txt"); string tmp; string lastLine(""); getline(in, tmp); while(!in.eof()) { lastLine = tmp; getline(in, tmp); } cout << "Letzte Zeile:\n" << lastLine; }
-
Wir sind hier nicht in C++ Herr Kollege.
U.g. sollte die letzte Zeile einer Textdatei ausgeben, auch korrekt, falls die Datei mit '\n' endet.
Schaue dir einfach erstmal nur die main-Funktion an, den Rest dann später.int getline(FILE *f,char **z) { int r=1,c; while( (c=fgetc(f))!=EOF ) { if(r==1) **z=0; if( c=='\n' ) return r-1; else { char ch=c; strncat(*z=realloc(*z,++r),&ch,1); } } return r-1?r-1:EOF; } int main() { FILE *f=fopen("bla.txt","rt"); char *zeile=malloc(1); setvbuf(f,0,_IOLBF,4096); while( getline(f,&zeile)!=EOF ); fclose(f); puts(zeile); free(zeile); return 0; }
-
Wutz schrieb:
Wir sind hier nicht in C++ Herr Kollege.
chicksgirl schrieb:
arbeite gerade etwas mit C und C++.
Welches ist denn das richtige Forum dafür, Herr Kollege?
-
Wir sind hier nicht in C++ Herr Kollege.
-
Lies mal richtig, Herr Kollege. Die Dame macht etwas in "C und C++". Wo ist bitte das C und C++ - Forum hier?
Das C++ - Forum wirst Du mir hier ja kaum empfehlen können, denn da "sind wir ja nicht in C".
-
Eigentlich sagt sie folgendes:
chicksgirl schrieb:
Lohnt es sich überhaupt das in C zu machen oder sollte man hier eher auf C++ umsteigen?
Die ideale C-Lösung wäre vermutlich folgende (und normalerweise wird sie schneller als die C++-Variante sein, weil nicht alles ausgelesen wird und vor allem benötigt sie nicht so viel Speicher):
#include <stdio.h> #include <stdlib.h> int main (int argc, char *argv[]) { FILE *f; long i; char c; if (argc != 2) { fprintf (stderr, "usage: %s filename\n", argv[0]); return 1; } f = fopen (argv[1], "r"); fseek (f, 0, SEEK_END); i = ftell (f) - 1; fseek (f, i, SEEK_SET); // Springe zum i-ten Zeichen if (i >= 1 && fgetc (f) == '\n') --i; // letzte (und nur letzte) Leerzeile überspringen do fseek (f, i, SEEK_SET); while (--i>=0 && fgetc (f) != '\n'); while ((c = fgetc (f)) != EOF && c != '\n') // f ist bei der letzten Zeile putchar (c); putchar ('\n') fclose (f); return 0; }
Ansonsten geht das auch, in dem man jede Zeile in einen
buf[255]
einliest und am Schluss den letzten ausgibt, ist vielleicht für einen Anfänger besser.
-
Soso, so sieht also vermutlich die ideale Lösung aus.
Ich vermute, sie produziert undefiniertes Verhalten,
der Inhalt von argv[0] ist undefiniert
bei Dateigrößen = 0 Byte ist die Lösung undefiniert
bei Dateigrößen > LONG_MAX ist die Lösung undefiniert
-
Wutz schrieb:
Soso, so sieht also vermutlich die ideale Lösung aus.
Ich gebe zu, das war etwas übertrieben.
Wutz schrieb:
Ich vermute, sie produziert undefiniertes Verhalten
Soweit würde ich aber nicht gehen
Wutz schrieb:
der Inhalt von argv[0] ist undefiniert
Nein, im schlimmsten Fall ist er 0. Der Standard definiert ein mit 0 abgeschlossenes argv[]-Array
Wutz schrieb:
bei Dateigrößen = 0 Byte ist die Lösung undefiniert
bei Dateigrößen > LONG_MAX ist die Lösung undefiniertDoch, sie ist definiert. ftell gibt bei einem Fehler -1 zurück, es wird nur noch ein fseek auf -1 ausgeführt, auch definiert.
Ich rate dir, auf meinei >= 1
bzw.--i >= 0
zu achten.
-
Der Inhalt von argv[0] ist undefiniert, das wollten andere vor dir hier auch nicht glauben: http://www.c-plusplus.net/forum/275028-10
Bei Dateigröße 0 liefert dein ftell 0L (und keinen Fehler), minus 1 ist -1L. -1L an fseek übergeben liefert für die 0 Byte große Datei undefiniertes Verhalten, zumal du den Rückgabewert gar nicht auswertest. Für >LONG_MAX Dateien springst du mit deinem fseek überall hin, nur nicht ans Dateiende. Und der _FILE_OFFSET_BITS Kram ist nicht ANSI C.
-
Der Standard definiert argc und argv wie folgt:
ISO/IEC 9899:1999 5.1.2.2.1 (2) schrieb:
If they are declared, the parameters to the main function shall obey the following
constraints:
— The value of argc shall be nonnegative.
— argv[argc] shall be a null pointer.
— If the value of argc is greater than zero, the array members argv[0] through argv[argc-1] inclusive shall contain pointers to strings, which are given implementation-defined values by the host environment prior to program startup. The intent is to supply to the program information determined prior to program startup from elsewhere in the hosted environment. If the host environment is not capable of supplying strings with letters in both uppercase and lowercase, the implementation shall ensure that the strings are received in lowercase.
— If the value of argc is greater than zero, the string pointed to by argv[0] represents the program name; argv[0][0] shall be the null character if the program name is not available from the host environment. If the value of argc is greater than one, the strings pointed to by argv[1] through argv[argc-1] represent the program parameters.
— The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination....woraus sich ergibt, dass argv[0] keinesfalls undefiniert ist. Allerdings kann es passieren, dass argc == 0 ist (etwa bei merkwürdige exec*-Aufrufen), in welchem Fall argv[0] == NULL und
fprintf (stderr, "usage: %s filename\n", argv[0]);
undefiniert ist.
Was das ursprüngliche Problem angeht, mit "Rest unverändert" meinte ich, dass die get_line-Funktion aus dem vorherigen Codestück unverändert übernommen werden sollte.
Die Kritik am fseek-Ansatz ist prinzipiell berechtigt; der C-Standard scheint sich nicht mit dem Gedanken zu befassen, dass Daten größer als LONG_MAX werden könnten und definiert die Fähigkeiten von fseek generell ziemlich eng. Da aber sowohl POSIX als auch die MSDN zusätzliches Verhalten definieren, welches die Anwendung von fseek vereinfacht, sollte man so ziemlich überall mit
fseek(fd, i, SEEK_END);
und negativem i davonkommen.
Allerdings ist das nicht unbedingt ein performanter Ansatz, zumindest nicht in der angegebenen Form, wo jeweils ein Zeichen zurückgesucht wird. Es hängt vom darunterliegenden Medium ab - nehmen wir, wegen der extremen Anschaulichkeit beispielsweise ein Kassettenlaufwerk und stellen uns vor, wie sich das verhielte, wenn man ihm ein "ein Zeichen vor, zwei Zeichen zurück"-Suchmuster gäbe. Wie auch bei der getline-Funktion scheint es sinnvoll, die Daten blockweise einzulesen und im Speicher nach Zeilenumbrüchen zu suchen.
Und Wutz, du solltest wirklich aufhören, Anfängern deine grauenvoll ineffiziente getline-Implementation aufs Auge zu drücken. Jemand könnte auf die Idee kommen, sie zu verwenden.
-
Sowohl realloc als auch fgetc/setvbuf arbeiten nicht zeichen- sondern blockweise, was Leute mit beschränktem Horizont leicht übersehen.
-
Hi Jungs
Also sorry, egal welchen eure Codes ich bei mir in Visual STudio reinhau, er sagt jedes mal beim debuggen okay und beim öffnen der Konsolenanwendung öffnet die Konsole kurz und schließt sich gleich wieder"The program '[5164] PPSTEST.exe: Native' has exited with code 0 (0x0)."
-
ist doch super, heißt soviel wie "alles richtig gemacht".
-
Wutz schrieb:
Sowohl realloc als auch fgetc/setvbuf arbeiten nicht zeichen- sondern blockweise, was Leute mit beschränktem Horizont leicht übersehen.
Nette Behauptung. Sie ist nur stumpf nicht wahr - lies es im Standard nach (7.20.3.4).
Das wäre dir übrigens auch aufgefallen, wenn du mal einen Debugger auf das Programm losgelassen hättest. Mit __FILE__ statt "blabla.txt" in deinem Code meint valgrind Folgendes:
==30558== Memcheck, a memory error detector ==30558== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al. ==30558== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info ==30558== Command: ./a.out ==30558== } ==30558== ==30558== HEAP SUMMARY: ==30558== in use at exit: 0 bytes in 0 blocks ==30558== total heap usage: [b]464 allocs, 464 frees, 6,509 bytes allocated[/b] ==30558== ==30558== All heap blocks were freed -- no leaks are possible ==30558== ==30558== For counts of detected and suppressed errors, rerun with: -v ==30558== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
...und das für 494 Byte in 32 Zeilen!
Du bist inzwischen lange genug dabei, um dir um Effizienz Gedanken zu machen und Behauptungen in diese Richtung zu prüfen, bevor du sie machst. Einen beschränkten Horizont lasse ich mir angesichts dieser Leistung von dir ganz bestimmt nicht vorwerfen.
@chicksgirl: Das sieht mir nach MSVC aus. Probier's mal mit Strg + F5 (wenn ich das gerade richtig im Kopf habe). Wenn du es mit "Debuggen" starten willst, setz halt einen Haltepunkt in die letzte Zeile.