Datei öffnen/einlesen, Häufigkeit berechnen
-
Hallo Leute
So nach den letzten Sachen sitze ich gerade wieder an einer neuen Geschichte 
Umgebung ist entsprechend wieder eine Consolen Anwendung
Und mein Programm soll folgendes tun (wenn es dann fertig ist ):Der User gibt einen Dateinamen ein (wird natürlich überprüft ob der vorhanden ist etc.) und es soll eine Datei geöffnet werden. Vorzugsweise eine .txt

Diese soll dann eingelesen werden und anschließend sollen alle Buchstaben (klein und groß kein Unterschied, sprich a = A => 1) und Zeichen/Zahlen in Bezug auf ihre Häufigkeit statistisch ausgegeben werden. Das Ganze im Optimalfall mit % Angaben

Ich hatte gerade schon mal ein paar Sachen eingetippt, so ist der aktuelle Stand:
#include "stdafx.h" #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string> #include <sstream> using namespace std; void freezeProg() { cout << "\n"; cout << "freezed - For leaving please push Return\n" << endl; cin.clear(); // Hier wird geprüft mit welche Taste gedrückt wurde, in dem Fall // wird nur bei Enter reagiert :) cin.ignore(std::cin.rdbuf()->in_avail()); cin.get(); } FILE *datei; int main(void) { string datei; cout << "++++++++++++ Willkommen ++++++++++++"; cout << "\n"; cout << "\n"; cout << "Bitte geben Sie den Namen der Datei ein, die Sie jetzt gerne öffnen wollen\n"; cout << "\n"; cin >> datei; cout << "\n"; cout << "Sie haben vor folgende Datei zu öffnen: " << datei; cout << "\n"; // Datei lesen, falls nicht möglich Versuch gescheitert // Ausgabe wird erzeugt, dass die Datei nicht geöffnet werden konnte err = fopen_s(&datei, "test.txt", "r"); if(err == 0) { printf("Die Datei wurde erfolgreich geöffnet!\n"); unsigned char buf[80]; int count[26]; int i, k, anz; for(i=0; i<26; i++) { count[i] = 0; printf("Anzahl: "); scanf_s("%d", &anz); for(k = 1; k <= anz; k++) { printf( "%d. Wort: ", k); scanf_s( "%s", buf); for(i = 0; buf[i] != 0; i++) count[buf[i]-'a']++; } printf( "\n Auswertung: \n"); for(i = 0; i <= 'z'-'a';i++) printf( "%c: %d\n", 'a'+i, count[i]); } } else { printf("Die Datei konnte nicht geöffnet werden!\n"); } if(datei) { err = fclose(datei); { printf("Die Datei wurde geschlossen\n"); } } else { printf("Die Datei konnte nicht geschlossen werden\n"); } // Alle Dateien schließen int numclosedfiles = _fcloseall(); printf("Anzahl der geschlossenen Dateien beträgt: %u\n", numclosedfiles); freezeProg(); return 0; }Habt ihr dazu vielleicht schon Vorschläge wie/ob man es besser machen kann?
Oder Tipps/Hilfen wie ich weiter vorgehen kann?
Hier tummeln sich ja Leute mit mehr Erfahrung und anderen Gedankengängen, deswegen bin ich über jegliches Feedback dankbar 
-
Ein Tipp wäre z.B., überhaupt mal aus der Datei zu lesen.
Übrigens, übergibst du da tatsächlich einen std::string* an fopen?? Das kriegst du doch aber nicht kompiliert, oder?
Wenn du Fehlermeldungen bekommst (und davon gehe ich mal ganz, ganz stark aus), dann solltest du die hier auch posten bzw. überhaupt mal darauf hinweisen. Im ersten Moment hört sich das eher nach "Mein Programm läuft - Verbesserungsvorschläge?" an...
EDIT:
- err ist nicht definiert
- if(datei) geht nicht, wenn datei ein std::string ist
- fopen und fclose gehen nicht mit std::string statt FILE-PointerDu solltest dir angewöhnen, die Doku zu Funktionen zu lesen. Da ist erklärt, welche Typen man übergeben muss. Und oft gibt es auch direkt ein Beispiel.
-
bmth schrieb:
#include "stdafx.h" // weg damit #include <iostream> #include <stdio.h> // sollte <cstdio> sein #include <stdlib.h> // sollte <cstdlib> sein #include <string> // brauchst auch nicht, iostream inkludiert string schon #include <sstream> using namespace std; // würd ich auch nicht empfehlen, würde eher using namespace in den fkt. benutzen void freezeProg() { cout << "\n"; cout << "freezed - For leaving please push Return\n" << endl; cin.clear(); // Hier wird geprüft mit welche Taste gedrückt wurde, in dem Fall // wird nur bei Enter reagiert :) cin.ignore(std::cin.rdbuf()->in_avail()); cin.get(); } FILE *datei; // böse global var, und FILE solltest du nicht benutzen, guggste: http://www.cplusplus.com/reference/iostream/iostream/ int main(void) // würd eher int main() oder int main(int argc, char *argv[]); schreiben { string datei; cout << "++++++++++++ Willkommen ++++++++++++"; cout << "\n"; cout << "\n"; cout << "Bitte geben Sie den Namen der Datei ein, die Sie jetzt gerne öffnen wollen\n"; cout << "\n"; cin >> datei; cout << "\n"; cout << "Sie haben vor folgende Datei zu öffnen: " << datei; cout << "\n"; // Datei lesen, falls nicht möglich Versuch gescheitert // Ausgabe wird erzeugt, dass die Datei nicht geöffnet werden konnte err = fopen_s(&datei, "test.txt", "r"); if(err == 0) { printf("Die Datei wurde erfolgreich geöffnet!\n"); unsigned char buf[80]; int count[26]; int i, k, anz; for(i=0; i<26; i++) { count[i] = 0; printf("Anzahl: "); scanf_s("%d", &anz); for(k = 1; k <= anz; k++) { printf( "%d. Wort: ", k); scanf_s( "%s", buf); for(i = 0; buf[i] != 0; i++) count[buf[i]-'a']++; } printf( "\n Auswertung: \n"); for(i = 0; i <= 'z'-'a';i++) printf( "%c: %d\n", 'a'+i, count[i]); } } else { printf("Die Datei konnte nicht geöffnet werden!\n"); } if(datei) { err = fclose(datei); { printf("Die Datei wurde geschlossen\n"); } } else { printf("Die Datei konnte nicht geschlossen werden\n"); } // Alle Dateien schließen int numclosedfiles = _fcloseall(); printf("Anzahl der geschlossenen Dateien beträgt: %u\n", numclosedfiles); freezeProg(); return 0; }Benutz nicht FILE, fopen, scanf_s, fclose: guggste http://www.cplusplus.com/reference/iostream/iostream/
-
Ach, jetzt seh ich erst, dass du zusätzlich zum lokalen std::string datei auch eine Globale datei hast. Das geht in dem Fall natürlich schief...
-
Habt ihr dazu vielleicht schon Vorschläge wie/ob man es besser machen kann?
Ja, "%" kommt aus der stdio.h und wurde in C zur Typenerkennung benutzt. Daher solltest du die C++-Funktionen aus der iostream benutzen.
-
_matze schrieb:
Ein Tipp wäre z.B., überhaupt mal aus der Datei zu lesen.
Übrigens, übergibst du da tatsächlich einen std::string* an fopen?? Das kriegst du doch aber nicht kompiliert, oder?
Wenn du Fehlermeldungen bekommst (und davon gehe ich mal ganz, ganz stark aus), dann solltest du die hier auch posten bzw. überhaupt mal darauf hinweisen. Im ersten Moment hört sich das eher nach "Mein Programm läuft - Verbesserungsvorschläge?" an...
EDIT:
- err ist nicht definiert
- if(datei) geht nicht, wenn datei ein std::string ist
- fopen und fclose gehen nicht mit std::string statt FILE-PointerDu solltest dir angewöhnen, die Doku zu Funktionen zu lesen. Da ist erklärt, welche Typen man übergeben muss. Und oft gibt es auch direkt ein Beispiel.
Das hatte ich aus einem Beispiel aus der MSDN Library. Weil ich hatte vorher fopen() benutzt, jedoch zeigt mir der Compiler dann natürlich an -> Warning: fopen ist nicht sicher, bitte fopen_s verwenden. So, danach bzw. nach einem Beispiel hatte ich dann gegooglet und so hatte ich eben den MSDN Link gefunden.
Aber wahrscheinlich habe ich dann was davon falsch verstanden / falsch umgesetzt.
http://msdn.microsoft.com/en-us/library/z5hh6ee9.aspx
Und stimmt, ich hatte das hier
errno_t err;ausversehen gelöscht. Aber ansonsten läuft es gerade durch, ohne Fehler. Ich poste noch mal den jetzigen Stand.
#include "stdafx.h" #include <iostream> #include <stdio.h> #include <stdlib.h> #include <string> #include <sstream> #include <fstream> using namespace std; void freezeProg() { cout << "\n"; cout << "freezed - For leaving please push Return\n" << endl; cin.clear(); // Hier wird geprüft mit welche Taste gedrückt wurde, in dem Fall // wird nur bei Enter reagiert :) cin.ignore(std::cin.rdbuf()->in_avail()); cin.get(); } FILE *datei; string dateiname; int main(void) { cout << "++++++++++++ Willkommen ++++++++++++"; cout << "\n"; cout << "\n"; cout << "Bitte geben Sie den Namen der Datei ein, die Sie jetzt gerne oeffnen wollen\n"; cout << "\n"; cin >> dateiname; cout << "\n"; cout << "Sie haben vor folgende Datei zu oeffnen: " << dateiname; cout << "\n"; // Datei lesen, falls nicht möglich Versuch gescheitert // Ausgabe wird erzeugt, dass die Datei nicht geöffnet werden konnte errno_t err; err = fopen_s(&datei, "test.txt", "r"); if(err == 0) { printf("Die Datei wurde erfolgreich geöffnet!\n"); unsigned char buf[80]; int count[26]; int i, k, anz; for(i=0; i<26; i++) { count[i] = 0; printf("Anzahl: "); scanf_s("%d", &anz); for(k = 1; k <= anz; k++) { printf( "%d. Wort: ", k); scanf_s( "%s", buf); for(i = 0; buf[i] != 0; i++) count[buf[i]-'a']++; } printf( "\n Auswertung: \n"); for(i = 0; i <= 'z'-'a';i++) printf( "%c: %d\n", 'a'+i, count[i]); } } else { printf("Die Datei konnte nicht geöffnet werden!\n"); } if(datei) { err = fclose(datei); { printf("Die Datei wurde geschlossen\n"); } } else { printf("Die Datei konnte nicht geschlossen werden\n"); } // Alle Dateien schließen int numclosedfiles = _fcloseall(); printf("Anzahl der geschlossenen Dateien beträgt: %u\n", numclosedfiles); freezeProg(); return 0; }
-
Das Problem war nicht fopen_s, sondern dass du einen lokalen std::string mit dem gleichen Namen ("datei") wie der globale FILE-Pointer hattest. Den hast du ja jetzt auch gelöscht.
Wenn das Programm jetzt läuft, ist es ja gut. Aber bislang machst du noch gar nichts mit der Datei...
Und sowas hier ist auch bedenklich:
for(i = 0; buf[i] != 0; i++) count[buf[i]-'a']++;Du verlässt dich darauf, dass in buf nur kleine Buchstaben stehen. Du wirst ganz leicht den gültigen Bereich für count verlassen. Überleg mal, welcher Index angewendet wird, wenn in buf nun ein 'A' (65) steht und du davon 'a' (97) abziehst...
-
Achso ich hatte das so verstanden, dass du an dem fopen_s was auszusetzen hast. Und jap bislang mache ich noch nichts mit der Datei, da wollte ich mich gleich ransetzen. Wollte nur fragen ob es generell an dem "Gerüst" was bisher vorhanden ist etwas zu bemängeln gibt

Das mit den Kleinbuchstaben stimmt schon, wobei es da ja mehrere Möglichkeiten gibt.
Da ein a dem A gleichwertig ist, könnte ich doch z.B. sagen, dass alle Großbuchstaben in kleine umgewandelt bzw. als solche angesehen und gezählt werden. Oder ich erweiter die Abfrage und kontrolliere auch auch Großbuchstaben und Zeichen/Zahlen.
Aber an ersterem gibt es doch eigentlich nichts auszusetzen oder seh ich dass falsch? Wie gesagt, gezählt werden ja beide gleich bzw. a und A entsprechend als ein (gleicher) Buchstabe.
Beispiel:
Das hier ist ein Test Aber...
A: 2
...
-
Es wäre vielleicht sinnvoll, dein count-Array auf alle ASCII/ANSI-Zeichen zu erweitern, also auf 256. Bei der anschließenden Analyse kannst du dann ja einfach alle Zeichen, die nicht vorgekommen sind, ignorieren. So kannst du einen falschen Index gleich vermeiden, ohne unnötig zu frickeln, und hast gleich alle Buchstaben und Zeichen (<256) drinnen.
Eine Anmerkung noch: Wieso notierst du den std::string und den FILE* als Globale? Mach das besser lokal in der main.
-
#include <fstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { ifstream in("datei.txt"); char z; unsigned zeichen[256]={ 0 }; while(!in.eof()) { in.get(z); zeichen[(int)tolower(z)]++; } for(int i=97;i<123;i++) { cout << char(i) << " " << zeichen[i] << endl; } in.close(); return 0; }
-
zl schrieb:
#include <fstream> #include <iostream> using namespace std; int main(int argc, char* argv[]) { ifstream in("datei.txt"); char z; unsigned zeichen[256]={ 0 }; while(!in.eof()) { in.get(z); zeichen[(int)tolower(z)]++; } for(int i=97;i<123;i++) { cout << char(i) << " " << zeichen[i] << endl; } in.close(); return 0; }bmth schrieb:
sollen alle Buchstaben (klein und groß kein Unterschied, sprich a = A => 1) und Zeichen/Zahlen in Bezug auf ihre Häufigkeit statistisch ausgegeben werden.
-
ja ist genau das.

-
Bzw. Die entsprechende Anpassung dürfte ja nicht das Problem sein.
-
zl schrieb:
Bzw. Die entsprechende Anpassung dürfte ja nicht das Problem sein.
Also das kommt immer auf die Person an, die anpassen will.

-
Wieso eigentlich keine
std::map? Und ASCII Subtraktionen sind sehr unsicher, da der Standard nicht garantiert, dass die Buchstaben hintereinander liegen.
Dastolowervon zl ist zwar bereits etwas besser, aber leider verwendet er das C Locale und nicht die C++ Version, was wiederum fehleranfällig sein könnte.Also ich wäre eher für sowas:
#include <map> #include <locale> #include <fstream> #include <iostream> #include <algorithm> void printCounter(std::pair<char, unsigned int> pair) { std::cout << pair.first << " => " << pair.second << std::endl; } int main() { char c; std::map<char, unsigned int> counter; std::locale locale; std::ifstream input("file.txt"); while(input.get(c)) { c = std::tolower(c, locale); ++counter[c]; } std::for_each(counter.begin(), counter.end(), &printCounter); return 0; }Grüssli

-
Erst mal vielen Dank für das Beispiel

Aber ich wollte da noch mal was nachfragen. Doch zunächst mal eine kleine Beschreibung von mir was in dem Beispiel passiert, ihr könnt mir dann ja sagen ob ich es richtig verstanden habe.
Mit dem "input.get(c))" hole ich mir entsprechend die Zeichen aus der Textdatei und "c" müsste dann meines Wissens nach den Wert vom letzten Zeichen haben, in meinem Fall ein "ß". Da, wenn ich es richtig nachgeguckt habe input auf "_FILEBUFFER" und "_MYFILE" zeigt.
Im Counter werden dann alle Zeichen aufgelistet mit Angabe der Position + Häufigkeit.
Dann noch zwei Erweiterungen an die ich mich rantasten wollte:
- Der Benutzer kann auswählen ob es sich um einen ASCII(mit .de Code Page) oder ANSI Text handelt
und 2)
Wenn die Statistik erzeugt wurde was die Häufigkeit angeht, dann soll der User als Ausgabe die Häufigkeit der einzelnen Buchstaben + Prozentzahl (2 Stellen hinter dem Komma) in Bezug auf die Anzahl aller Zeichen ausgegeben.Zum ersten Punkt:
Da kann ich doch zunächst mal mit scanf oder cin z.B. die Eingabe des Users abfangen und die bei der weiteren Verarbeitung doch eigentlich auch als Referenz übergeben oder?
-
Mh habe gerade ein Formatierungsproblem. Hat da jemand eine Idee?
error C2440: '=' : cannot convert from 'FILE **' to 'wchar_t *'Hier der entsprechenden Code Teile:
int main(int argc, char *argv[]) { FILE *datei; char c; wchar_t *str; str = &datei; int numclosedfiles = _fcloseall(); cout << "++++++++++++ Willkommen ++++++++++++"; cout << "\n"; cout << "\n"; cout << "Bitte geben Sie den Namen der Datei ein, die Sie jetzt gerne oeffnen wollen\n"; cout << "\n"; //cin >> str; cout << "\n"; //cout << "Sie haben vor folgende Datei zu oeffnen: " << str; cout << "\n"; // Datei lesen, falls nicht möglich Versuch gescheitert // Ausgabe wird erzeugt, dass die Datei nicht geöffnet werden konnte errno_t err; err = fopen_s(&datei, "test.txt", "r"); if(err == 0) { cout << "Die Datei wurde erfolgreich geoeffnet!\n"; cout << "\n"; map<char, unsigned int> counter; locale locale; ifstream input("test.txt"); while(input.get(c)) { c = tolower(c, locale); ++counter[c]; } for_each(counter.begin(), counter.end(), &printCounter); input.close();
-
Ich hab ne Idee: nimm den wchar_t* raus! Dein FILE* ist ein Handle, kein Dateiname, falls du das denkst.
-
Ist schon raus, war ja auch unsinnig.
Ok, jetzt bin ich glaube ich wieder einen Schritt weiter. Jetzt übergibt er mir von meiner Eingabe "test.txt" schon mal das "t" ^^.
Nur habe ich das ja jetzt gerade "fest eingetragen".
FILE *datei; char c; char str; int numclosedfiles = _fcloseall(); cout << "++++++++++++ Willkommen ++++++++++++"; cout << "\n"; cout << "\n"; cout << "Bitte geben Sie den Namen der Datei ein, die Sie jetzt gerne oeffnen wollen\n"; cout << "\n"; cin >> str; cout << "\n"; cout << "Sie haben vor folgende Datei zu oeffnen: " << str; cout << "\n"; // Datei lesen, falls nicht möglich Versuch gescheitert // Ausgabe wird erzeugt, dass die Datei nicht geöffnet werden konnte errno_t err; char *pstr; pstr = &c; err = fopen_s(&datei, "test.txt", "r"); if(err == 0) { cout << "Die Datei wurde erfolgreich geoeffnet!\n"; cout << "\n"; map<char, unsigned int> counter; locale locale; ifstream input("test.txt"); while(input.get(c)) { c = tolower(c, locale); ++counter[c]; } for_each(counter.begin(), counter.end(), &printCounter);Wie kann ich dass denn jetzt dynamisch machen? Also sprich das er die Eingabe des Users (am besten mit Kontrolle auf das Format, von wegen XYZ.txt) an FILE*datei übergibt?
-
Ein char ist nicht dasselbe wie ein char*! Du definierst str als char, das ist ein einzelnes Zeichen (daher kriegst du nur das 't')! Eine Zeichenkette musst du anders definieren, z.B. "char str[50]". So legst du ein Array of chars (also einen String, eine Zeichenkette) mit der Länge 50 an. Denk dran, dass da auch nur 49 Zeichen und die abschließende Null reinpassen!
Und an FILE übergeben musst du gar nix, zumindest nicht so wie du denkst. Du musst die Funktion fopen verwenden und den Rückgabe-Wert deinem FILE-Pointer zuweisen. Alternativ gibt es auch die bereits erwähnte C++-Variante mit Streams (ifstream, ofstream).