Folgenden Code "professioneller"?
-
Hallo an euch!
Ich habe eine Textdatei, in welcher ich die Newline-Chars schon entfernt habe.
Sie ist nun eine lange Einzeiler-DateiIch möchte nun schauen, ob in der Datei die Buchstabenreihenfolge "GATTACA" vorhanden ist. Es ist eine Aufgabe die ich im Internet gefunden habe.
Das funktioniert auch alles so wie es soll, allerdings finde ich, dass der Code wenig schön aussieht. Gibt es eine andere Möglichkeit sodass es mehr "professionell" aussieht? Dachte schon an Switch-Statements..
Natürlich kann ich die if-Statements in eine Zeile ballern, aber dann wirds unleserlich.Anmerkung für die do-while-Schleife:
Ich habe das Array mal auf eine Größe von 1 Millionen gesetzt, da man eine Datei "inputten" kann. Vermeiden will ich damit den Buffer-overflow/Array out of bound, dass wirklich nur G, A, T und C als Buchstabe verwendet wird.#include <stdio.h> int main (void){ char c[1000000]; int i = 0; int n; int count = 0; fgets(c, sizeof(c), stdin); puts("Aufgenommen.."); //printf("Output:\n%s\n", c); do{ if (c[i] == 'G'){ n = i; n++; if (c[n] == 'A'){ n++; if (c[n] == 'T'){ n++; if (c[n] == 'T') { n++; if (c[n] == 'A'){ n++; if (c[n] == 'C'){ n++; if (c[n] == 'A'){ /* Wort GATTACA gefunden? Dann erhöhe "count" um 1 */ count++; } } } } } } } i++; /* Wiederhole solange, bis der Buchstabe kein G, T, A oder C ist */ }while(c[i] == 'G' || c[i] == 'A' || c[i] == 'T' || c[i] == 'C'); puts("\n"); printf("%d\n", count); return 0; }
-
Dein Code liest die zu durchsuchenden Daten von der Standardeingabe ein ... hm. Kann man machen. Auf der Kommandozeile kann man das Programm dann entweder so:
echo "Meine Daten" | mein_program
oder so:
cat "Meine Datei" | mein_program
aufrufen. Bin mir allerdings nicht sicher, ob dadurch unnötige Kopien entstehen - vielleicht solltest du also auf einfache Dateinamen wechseln.
Ein Problem ist deine 1-MB-Grenze. Du solltest dir
malloc
undrealloc
anschauen. Der Stack ist arg limitiert und für solch große Datenmengen nur mit zusätzlichem Aufwand (Commitgröße vom Linker) ausgerichtet. Oder du lädst immer nur Teile von der Standardeingabe in einen Buffer und trägst dann immer das Ende mit in die nächste Iteration, das ist dann noch sogar D-Cache-freundlich.Für die Stringsuche solltest du deinen eigenen Boyer-Moore schreiben. GNU/Linux-Systeme definieren zwar
memmem
, aber das ist messbar langsamer (weil jedes Byte durchsucht wird, anstatt mit einer Maske bestimmte Mengen an Zeichen skippen zu können) und gibt es zudem nicht auf Windows. Das packst du dann am Besen aber in eine eigene Funktion.EDIT: Alternativ kannst du dir auch
strstr
anschauen, die Funktion wird von C89 definiert und sollte auch auf Windows-Systemen verfügbar sein. Aber um diese ganzenstrXXX
-Funktionen mache ich sonst lieber einen großen Bogen. NUL-Terminierung ist doof, undstrXXX
benötigen diese.EDIT 2: "zu suchen" durch "zu durchsuchen" ersetzt - Hirnfurz.
-
dachschaden schrieb:
cat "Meine Datei" | mein_program
Ein Fall für den UUOC-Award? Besser:
mein_programm < meine_datei.txt
Ansonsten ist https://de.wikipedia.org/wiki/Boyer-Moore-Algorithmus sicherlich eine gute Idee. Oder schau dir https://de.wikipedia.org/wiki/Knuth-Morris-Pratt-Algorithmus an.
Du brauchst allerdings nicht die komplette Datei in den RAM zu laden (was passiert, wenn du in einer riesigen Datei suchst?). Also lies Abschnitte ein, durchsuche diese und lies dann weiter. Du musst dann nur an den Grenzen aufpassen, dass das Suchwort auch gefunden wird, wenn es die Einlesegrenze überlappt.
-
Datei stückweise einlesen. Stückgröße nicht zu groß, paar tausend Zeichen. Stück mit der Standardbibliothek durchsuchen! Darauf achten, dass das Wort auch an der Grenze zwischen Stücken stehen kann. Daher (Länge des Suchbegriffs - 1) Zeichen vom vorherigen Stück behalten.
-
Wähle einen streambasierten Ansatz und für eine absehbar große aber unbekannte Länge des Suchstrings eignet sich Rekursion:
int searchnext(FILE*f, const char*s) { int c; fpos_t p; fgetpos(f, &p); c = fgetc(f); if (c == EOF) return 0; if (tolower((unsigned char)c) == tolower((unsigned char)*s)) { if (!*++s) return 1; else return searchnext(f, s); } fsetpos(f, &p); return 0; } int searchfor(FILE*f, const char*s) { int c; while ((c = fgetc(f)) != EOF) { if (tolower((unsigned char)c) == tolower((unsigned char)*s) && searchnext(f, s + 1)) return 1; } return 0; } int main(int argc,char**argv) { return searchfor(stdin, argv[1]); }
programm "gattaca" < test.txt echo %ERRORLEVEL% bzw. echo $?
-
Wutz schrieb:
Wähle einen streambasierten Ansatz und für eine absehbar große aber unbekannte Länge des Suchstrings eignet sich Rekursion
Warum? Das hat nur jede Menge Funktionsoverhead. Das ist ein Problem, welches sich wunderbar iterativ lösen lässt.
-
Ein ganz anderer Aspekt noch:
angenommen, du würdest statt nach GATTACA nur nach ATTACA suchen. Wie viele Treffer hätte dann "ATTACATTACA" - einen oder zwei? Oder anders gefragt: willst du Überlappungen zulassen?
-