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-Datei 😃

    Ich 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 und realloc 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 ganzen strXXX -Funktionen mache ich sonst lieber einen großen Bogen. NUL-Terminierung ist doof, und strXXX 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.


  • Mod

    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]);
    }
    

    http://ideone.com/yeRlZ9

    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?




Anmelden zum Antworten