C scanf-Befehl wird in do-while-Schleife beim ersten Durchlauf übersprungen?

DrToxic

Rear Admiral
Registriert
Juni 2008
Beiträge
6.068
Hey,

ich bin gerade dabei, ein Programm zu erstellen, welches einen komplementären DNA-Strang zu einem eingegebenen Strang ausgibt.

An sich ist das einfach, aus A wird T, aus G wird C und umgekehrt.

Als ersten Versuch wollte ich Arrays mal außen vor lassen und nur einen einzelnen Buchstaben umwandeln lassen, hat soweit auch ganz gut geklappt, mit switch etc., ihr kennt den Kram.

Jetzt wollte ich halt nur eine DAU-Sicherheitsschleife bei der Eingabe einbauen, welche einen Error ausgibt, sobald man am Anfang was anderes als A, T, G oder C eingibt.

Vereinfacht gesagt, sowas hier - habe es zum Testen mal komplett miniaturisiert, um Fehler auszuschließen:
Code:
#include <stdio.h>
void main (void){
    char eingang;
    do {
        printf ("a eingeben:\n");
        scanf("%c", &eingang);
        if (eingang!='a'){
            printf("error\n");
        }
    } while(eingang!='a');
    printf("success!\n");
}
Sobald man was anderes als a eingibt, kommt halt die Fehlermeldung und er soll von vorn beginnen d.h. nochmal abfragen etc, bis er halt einen Wert hat, mit dem er weiterarbeiten kann (sprich: a).

Das Doofe ist jetzt nur, dass er bei einer falschen Eingabe die Schleife zwar von vorn beginnt, jedoch dabei den scanf-Befehl auslässt, dann die Schleife nochmal beginnt und dann erst abfragt.

Ich habe da echt keine Erklärung für :freak:

Das passiert aber nur bei chars. Wenn ich eingang als int definiere und eine Zahlenabfrage mache, klappt's seltsamerweise ohne Probleme.

Screen im Anhang
 
Du kannst den Tastaturpuffer mit
Code:
fflush(stdin);
leeren
 
Nach der ersten Eingabe bleibt das "Enter" im Tastaturpuffer und wird beim zweiten Aufruf direkt übermittelt und somit keine Eingabe wahrgenommen, da für den Rechner scheinbar sofort "Enter" gedrückt wurde. Somit muss der Tastaturpuffer gelöscht werden, damit neue Eingaben ordentlich verarbeitet werden können.
 
Du gibst ja "a" + <Enter> ein.

Damit steht im Tastaturpuffer "a\n", wobei '\n' für New Line steht. Evtl ist es auch '\r' (Carriage return), da bin ich mir jetzt nicht ganz sicher, aber das ist ja auch egal.

scanf liest nun ein char, nämlich 'a' aus dem Puffer, beim nächsten mal wird '\n' aus dem Puffer gelesen, da das ja auch noch drinnen steht und genau einem Char entspricht. Damit hast du deine Geister-Eingabe.
 
Im zweiten Schleifendurchlauf wird (bei mir getestet) ein Linefeed 0x0A bzw. dezimal 10 verarbeitet. Das liegt, wie snow1 bereits sagte, daran dass sich dieses Zeichen noch im Tastaturpuffer befindet. Bei scanf wird als erster Parameter ein Format übergeben. statt

fflush(stdin);

kannst du auch

scanf("%c\n", &eingang);

benutzen um den gleichen Effekt zu erzielen. Bei int Werten, also %d im Format, sorgt das %d Format dafür, das Whitespaces, also Leerzeichen, Tabs, Linefeeds automatisch überlesen werden. Das ist der Unterschied.
 
Aaah, alles klar.

Lustigerweise war das meine erste Idee, die ich aber sofort wieder verworfen hatte, weil beim zweiten mal eben keine leere Zeile erscheint, die man bei einer Eingabe des "\n" eigentlich erwarten würde.

Das erklärt aber natürlich auch, wieso das bei ints & floats nicht auftritt - danke für die schnelle Hilfe :freaky:

r0b0t schrieb:
scanf("%c\n", &eingang);
Die Lösung finde ich sogar noch eleganter, danke dafür!

Edit: Ich bin's nochmal, diesmal mit einem anderen Problem:

Wie gesagt, wollte ich ja komplementäre Basenpaare ermitteln. Da es bei der DNA jedoch Thymin, bei der mRNA diese Funktion jedoch von Uracil übernommen wird und ich beides abdecken will (sprich: Rückfrage, ob man in DNA oder mRNA konvertieren möchte), brauche ich da eine Abfrage.

Ich hatte es erst in dem Stil von "für DNA eine 1 eingeben, für mRNA eine 2 eingeben", habe scanf("%d" &i) gemacht und habe einen switch für int i gesetzt.

Das war mir aber zu unelegant und ich hätte gern, dass nur die Frage kommt "In was soll umgewandelt werden?" und man dann entweder DNA oder mRNA eintippen kann.

So weit, so gut. Habe mich dann in arrays, strings und die fgets-Funktion eingelesen und zum Testen kam ca. folgendes raus:

Code:
#include <stdio.h>
void main (void){
    char umwandlung[5];
        printf ("Uebersetzen in DNA oder mRNA?\n");
        fgets (umwandlung, 5, stdin);
        fflush(stdin);
        printf("Es wird umgewandelt in %s", umwandlung);
Das funktioniert wunderbar, er gibt mir aus, ob in DNA oder mRNA umgewandelt wird.

Sobald ich jetzt aber versuche, irgendwas mit if, while, switch oder sonstwas zu machen, erkennt er offenbar den Inhalt von umwandlung[5] anders als er mit printf ausgegeben wird.

z.B. hier:
Code:
#include <stdio.h>
void main (void){
    char eingang;
    char umwandlung[5];
    do {
        printf ("Uebersetzen in DNA oder mRNA?\n");
        fgets (umwandlung, 5, stdin);
        fflush(stdin);
        printf("Es wird umgewandelt in %s", umwandlung);
    } while (umwandlung[5]!='DNA' && umwandlung[5]!='mRNA');
}
Das hier wird zur Endlosschleife, auch wenn man DNA oder mRNA eingegeben hat.

Ich hatte schon vermutet, dass es daran liegt, dass der String mehr als Zeichen hat als die, die man eingibt, d.h. im Fall von 'DNA' dann ' D, N, A, 0, \0" und bei 'mRNA' = 'm, R, N, A, \0", aber ich weiß nicht, wie ich das Problem umgehen soll.

Edit²: Irgendwie scheint es auch hier Probleme mit \n zu geben, die in den string mit rein genommen zu werden, hab ich gerade gemerkt.
Da hilft mir allerdings auch fflush nicht weiter, weil fgets das Enter direkt mit rein nimmt :-S
 
Zuletzt bearbeitet:
Ja, so wird das nix ;)

Bisher hast du immer mit Chars ('a', 'b', 'c', ...) gearbeitet, nun brauchst du aber Strings ("hallo", "ich bin ein String", ...). Das sind im Grund auch auch nur Ketten von Chars, aber da sieht die Sache etwas anders aus.

Vergleiche ala == funktioniert nur auf einzelne Zeichen. Strings benötigen Methoden wie strcmp(). Das vergleicht Strings und liefert bei Gleichheit eine 0 zurück.

z.B.
Code:
if (strcmp(variable, "hallo") == 0) { machwas }
 
snow1 schrieb:
Vergleiche ala == funktioniert nur auf einzelne Zeichen. Strings benötigen Methoden wie strcmp(). Das vergleicht Strings und liefert bei Gleichheit eine 0 zurück.

Ok, das hatte ich schon gelesen, aber irgendwie gehofft, dass ==-Vergleiche trotzdem noch klappen :p

Die große Preisfrage ist jetzt natürlich, wie ich das in einem Switch benutzen kann.
Klar könnte ich stattdessen auch einen ganzen Haufen if-Bedingungen nehmen, aber wirklich elegant ist das nicht :)

Ich möchte das ca. so machen:
Code:
do {
        printf ("Uebersetzen in DNA oder mRNA?\n");
        fgets (umwandlung, 5, stdin);
        switch (umwandlung[5]){
            case 'DNA' : switch (eingang) {
                case 'A' : printf("T\n");
                    break;
                case 'T' : printf("A\n");
                    break;
                case 'C' : printf("G\n");
                    break;
                case 'G' : printf("C\n");
                    break;
            }
                     break;
            case 'mRNA' : switch (eingang) {
                case 'A' : printf("U\n");
                    break;
                case 'T' : printf("A\n");
                    break;
                case 'C' : printf("G\n");
                    break;
                case 'G' : printf("C\n");
                    break;
            }
                     break;
            default : printf("Ungueltige Eingabe\n\n");
                    break;
        }
        fflush(stdin);
    } while (strcmp(umwandlung, "DNA") == 0 && strcmp(umwandlung, "mRNA") == 0);

Weiß aber gerade nicht genau, wie.

Ich geh erstmal abend essen, dann sehen wir weiter :)
 
Mache dir das doch nicht so schwer (es sei denn, du willst gerade Programmieren lernen). Sonst gehe den Weg des geringsten Widerstands und ändere deine Abfrage in:

printf ("Uebersetzen in DNA oder mRNA? (d/m)\n");

Der Benutzer gibt dann einfach 'd' oder 'm' ein. Diese Verarbeitung eines einzelnen Zeichens kannst du ja schon ;)

In deinem Beispiel ist umwandlung[5] das fünfte Zeichen in dem Feld (array) umwandlung. Gibt der Benutzer "DNA" ein steht dort ein zufälliger Wert. Bei Eingabe von "mRNA" steht dort ein Linefeed (0x0A). Zeichenketten (char arrays) funktionieren in C nicht in einer switch Anweisung. Möglich wäre nur das erste Zeichen abzufragen:

Edit 18.10.2010: 9:35 Falsche Erklärung
umwandlung[5] ist natürlich nicht das fünfte Element im array. Da das Feld ja bei 0 beginnt und als char umwandlung[5] definiert ist, gibt es ja nur die Elemente [0] [1] [2] [3] [4]. Mit umwandlung [5] greift man also immer auf einen Speicherbereich zu, der gar nicht definiert ist. Das ist aber ein beliebter Fehler, der gern gemacht wird :)

Code:
switch (umwandlung[0]){
            case 'D' :  ...

                     break;
            case 'm' : ...

                     break;

            default : printf("Ungueltige Eingabe\n\n");
                    break;
}

Deine do while Schleife hat eine Bedingung, die immer falsch ergibt:
(strcmp(umwandlung, "DNA") == 0 && strcmp(umwandlung, "mRNA") == 0);
 
Zuletzt bearbeitet: (Falsche Beschreibung)
r0b0t schrieb:
Mache dir das doch nicht so schwer (es sei denn, du willst gerade Programmieren lernen).
Korrekt - ein Wunder, dass man das bisher noch nicht gemerkt hat :D

An sich könnte ich natürlich auch einfach zwei oder drei if-Bedingungen anstatt des switches setzen, aber das wären zusätzliche Prüfungen, also zusätzlicher Rechenaufwand, also unelegant ;)


r0b0t schrieb:
printf ("Uebersetzen in DNA oder mRNA? (d/m)\n");

Der Benutzer gibt dann einfach 'd' oder 'm' ein. Diese Verarbeitung eines einzelnen Zeichens kannst du ja schon ;)
Wie gesagt, das hatte ich schon, nur halt mit 1 und 2 statt d und m - fand ich aber nicht so toll :)


r0b0t schrieb:
In deinem Beispiel ist umwandlung[5] das fünfte Zeichen in dem Feld (array) umwandlung.
Oha, das erklärt einiges.. Ich dachte natürlich direkt mal, dass man das [5] immer dran hängen muss.. Das erklärt die ganzen Speicherzugriffs-Errors :lol:

r0b0t schrieb:
Zeichenketten (char arrays) funktionieren in C nicht in einer switch Anweisung.
Möp.. Schade :(

r0b0t schrieb:
Möglich wäre nur das erste Zeichen abzufragen:
Das wäre der beste Kompromiss, denke ich.
Also einfach das erste Zeichen mit umwandlung[0] auslesen?

edit: kann man so machen, hat aber einen unangenehmen Nachteil:
Wenn man z.B. "Doof" eingibt, gibt der Switch keine ungültige Eingabe aus, aber die Schleifenabbruchbedingung ist trotzdem nicht erfüllt.
Ich mag keine Loopholes, von daher habe ich es jetzt wie vorgeschlagen mit d und m gemacht.

Sehr ärgerlich, dass C keine Strings in Schleifen vergleichen kann :(
Oder gibt es dafür vielleicht ein header-file, dass erweiterte switches anbietet?
(sowas wie string.h für strings, halt nur für switches :D )

r0b0t schrieb:
Deine do while Schleife hat eine Bedingung, die immer falsch ergibt:
(strcmp(umwandlung, "DNA") == 0 && strcmp(umwandlung, "mRNA") == 0);
Sorry, war in Eile. Soll natürlich bei beiden != heißen, damit man eine weder-noch-Bedingung erhält :)
 
Zuletzt bearbeitet:
(es sei denn, du willst gerade Programmieren lernen).
Korrekt - ein Wunder, dass man das bisher noch nicht gemerkt hat
Ich dachte halt, diese DNA Umwandlung wäre dein eigentliches Problem und du suchst nur eine schnelle Lösung dafür.
Oder gibt es dafür vielleicht ein header-file, dass erweiterte switches anbietet?

In C gibt es das nicht, aber in C# kann man so programmieren:

Code:
switch (eingabe) {
  case "DNA": ...
    break;
  case "mRNA": ...
    break;
  default: ...
    break;
}

in C nimmt man halt eine if else Kaskade:

Code:
if (strcmp(eingabe,"DNA") == 0) {
  ...
} else if (strcmp(eingabe,"mRNA") == 0) {
  ...
} else {  /* default */
  ...
}
 
r0b0t schrieb:
Ich dachte halt, diese DNA Umwandlung wäre dein eigentliches Problem und du suchst nur eine schnelle Lösung dafür.
Naja, es war schon zielorientiert :)
Aber weil ich halt relativ neu bin, checke ich gern ab, was man sonst noch für Möglichkeiten hat, um mal ein Gespür für das Machbare zu bekommen.


r0b0t schrieb:
In C gibt es das nicht, aber in C# kann man so programmieren:
Jo, genau so hätte ich das gern. Das mit der if-Kaskade funktioniert natürlich auch, aber wirklich schön ist echt was anderes..

Naja, ich werd mich erstmal vernünftig in C einarbeiten und früher oder später eh zu C# bzw C++ wechseln - wobei ich auch da noch nicht wirklich weiß, wo die Unterschiede sind.. Wird sich alles noch zeigen :)

Danke erstmal für die Hilfe, hier sind meine beiden fertigen Programme, falls sich jemand für das Ergebnis interessiert:

einzelne Base:
Code:
#include <stdio.h>
void main (void){
    char eingang, umwandlung;
    do {
        printf ("Eingangsbase eingeben: ");
        scanf("%c", &eingang);
        fflush(stdin);
        if (eingang!='A' && eingang!='T' && eingang!='C' && eingang!='G'){
            printf("Bitte geben Sie eine gueltige Base ein!\nA = Adenin\nT = Thymin\nC = Cytosin\nG = Guanin\n");
        }
    } while(eingang!='A' && eingang!='T' && eingang!='C' && eingang!='G');
    do {
        printf ("\nKomplementaerstrang als DNA oder mRNA?\nBitte d oder m eingeben: ");
        scanf ("%c", &umwandlung);
        fflush(stdin);
        switch (umwandlung){
            case 'd' : switch (eingang) {
                case 'A' : printf("\nkomplementaere Base ist: T\n\n");
                    break;
                case 'T' : printf("\nkomplementaere Base ist: A\n\n");
                    break;
                case 'C' : printf("\nkomplementaere Base ist: G\n\n");
                    break;
                case 'G' : printf("\nkomplementaere Base ist: C\n\n");
                    break;
            }
                     break;
            case 'm' : switch (eingang) {
                case 'A' : printf("\nkomplementaere Base ist: U\n\n");
                    break;
                case 'T' : printf("\nkomplementaere Base ist: A\n\n");
                    break;
                case 'C' : printf("\nkomplementaere Base ist: G\n\n");
                    break;
                case 'G' : printf("\nkomplementaere Base ist: C\n\n");
                    break;
            }
                     break;
            default : printf("\nUngueltige Eingabe\n\n");
                    break;
        }
    } while (umwandlung!='d' && umwandlung!='m');
    getchar ();
}
Basenstrang:
Code:
#include <stdio.h>
#include <string.h>
void main (void){
    int i, j, probe, fehler;
    const int a=256;
    char umwandlung, strang[a];
    do {
        printf("Ausgangsstrang eingeben\n(Nicht mehr als %d Zeichen)\n", a-1);
        fgets (strang, a, stdin);
        fflush(stdin);
        probe=0, fehler=0;
        for(i=0; i < a && probe!=1 && fehler!=1; i++) {
            if (strang[i]=='\n') {
                strang[i]='\0';
                probe=1;
            }
            if (strang[i]!='A' && strang[i]!='T' && strang[i]!='C' && strang[i]!='G' && strang[i]!='\0'){
                printf("\nEingabefehler an Position %d!\nBitte geben Sie gueltige Basen ein!\n\nA = Adenin\nT = Thymin\nC = Cytosin\nG = Guanin\n\n", i+1);
                fehler=1;
            }
        }
    } while (fehler==1);
    do {
        probe=0;
        printf ("\nKomplementaerstrang als DNA oder mRNA?\nBitte d oder m eingeben: ");
        scanf ("%c", &umwandlung);
        fflush(stdin);
        switch (umwandlung){
            case 'd' : for (i=0, j=strlen(strang); i < j; i++){
                        switch (strang[i]) {
                            case 'A' : strang[i]='T';
                                break;
                            case 'T' : strang[i]='A';
                                break;
                            case 'C' : strang[i]='G';
                                break;
                            case 'G' : strang[i]='C';
                                break;
                            case '\0': strang[i]='\0';
                                break;
                        }
                       }
                break;
            case 'm' : for (i=0, j=strlen(strang); i < j; i++){
                        switch (strang[i]) {
                            case 'A' : strang[i]='U';
                                break;
                            case 'T' : strang[i]='A';
                                break;
                            case 'C' : strang[i]='G';
                                break;
                            case 'G' : strang[i]='C';
                                break;
                            case '\0': strang[i]='\0';
                                break;
                        }
                       }
                break;
            default : printf("\nUngueltige Eingabe\n\n");
                    break;
        }
    } while (umwandlung!='d' && umwandlung!='m');
    printf("\nKomplementaerstrang ist:\n%s\n\n", strang);
    getchar();
}
 
Zuletzt bearbeitet:
Ich würde mir nicht angewöhnen, IF Konstrukte als hässlich zu bezeichnen, denn du wirst einfach nicht drumherum kommen, sie zu benutzen ;)

Switch ist zwar schön und gut (und hat auch absolut seine Berechtigung), kann aber, wie du gesehen hast, einfach nicht immer alles abdecken.


so long
 
Zurück
Oben