2010-09-16 12 views
6

Wenn ich 5 5 am Terminal eingeben, drücken Sie die Eingabetaste, und drücken Sie erneut die Eingabetaste, ich möchte die Schleife beenden.Erhalten scanf, um zu beenden, wenn es einen Zeilenumbruch liest?

int readCoefficents(double complex *c){ 
    int i = 0; 
    double real; 
    double img; 
    while(scanf("%f %f", &real, &img) == 2) 
     c[i++] = real + img * I; 


    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
} 

Offensichtlich, dass Code macht den Job für mich nicht (und ja, ich weiß, dass es ein Pufferüberlauf ist warten zu geschehen).

scanf wird nicht beendet, es sei denn, ich schreibe einen Buchstaben (oder einige nicht-numerische, nicht Leerzeichen Zeichenfolge). Wie bekomme ich scanf, um nach dem Lesen einer leeren Zeile zu beenden?

+0

Falls Sie mehr Gründe brauchten, '' scanf' zu vermeiden: http://c-faq.com/stdio/scanfprobs.html – jamesdlin

Antwort

7

Verwenden fgets Konsole Eingang zu lesen:

int res = 2; 
    while (res == 2) { 
     char buf[100]; 
     fgets(buf, sizeof(buf), stdin); 
     res = sscanf(buf, "%f %f", &real, &img); 
     if (res == 2) 
      c[i++] = real + img * I; 
    } 
    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
+0

Ich gebe Ihnen den Kredit, da Sie zuerst fgets und sscanf erwähnt haben. – Matt

+0

Danke, gerade realisiert c wird wahrscheinlich nicht zulassen, dass Sie den Puffer innerhalb der While-Schleife deklarieren, aber das Konzept ist da. – MattSmith

+2

Diese Erklärung ist völlig in Ordnung. – jamesdlin

9

Das spezifische Problem ist, die Sie haben, dass ein scanf Format-String von %f Leerraum überspringen wird (einschließlich Zeilenumbrüche), bis sie einen tatsächlichen Charakter findet auf Scan. Vom C99-Standard:

Eine Umwandlungsspezifikation in den folgenden Schritten ausgeführt wird:
     -      Eingangsleerraumzeichen (wie durch die isspace Funktion angegeben) werden übersprungen, es sei denn die Beschreibung umfasst ein '[', 'c' oder 'n' Spezifizierer.

und anderswo, beschreibt isspace():

Die Standard-Leerraumzeichen die folgenden sind: '\t' Raum ' ', Formularvorschub '\f', new-line '\n', Wagenrücklauf '\r', horizontal tab, und vertikale Registerkarte .

Ihre beste Wette ist fgets zu verwenden, um die Linie zu bekommen (und das kann von Pufferüberlauf sehr leicht geschützt werden), dann sscanf auf der resultierenden Linie verwenden.

Die scanf Funktion ist eine jener, die Sie sehr vorsichtig betrachten sollten. Das folgende Stück Code ist, den ich verwende, um oft Line-Eingang handhaben:

#include <stdio.h> 
#include <string.h> 

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
static int getLine (char *prmpt, char *buff, size_t sz) { 
    int ch, extra; 

    // Get line with buffer overrun protection. 
    if (prmpt != NULL) { 
     printf ("%s", prmpt); 
     fflush (stdout); 
    } 
    if (fgets (buff, sz, stdin) == NULL) 
     return NO_INPUT; 

    // If it was too long, there'll be no newline. In that case, we flush 
    // to end of line so that excess doesn't affect the next call. 
    if (buff[strlen(buff)-1] != '\n') { 
     extra = 0; 
     while (((ch = getchar()) != '\n') && (ch != EOF)) 
      extra = 1; 
     return (extra == 1) ? TOO_LONG : OK; 
    } 

    // Otherwise remove newline and give string back to caller. 
    buff[strlen(buff)-1] = '\0'; 
    return OK; 
} 

 

// Test program for getLine(). 

int main (void) { 
    int rc; 
    char buff[10]; 

    rc = getLine ("Enter string> ", buff, sizeof(buff)); 
    if (rc == NO_INPUT) { 
     // Extra NL since my system doesn't output that on EOF. 
     printf ("\nNo input\n"); 
     return 1; 
    } 

    if (rc == TOO_LONG) { 
     printf ("Input too long [%s]\n", buff); 
     return 1; 
    } 

    printf ("OK [%s]\n", buff); 

    return 0; 
} 

Testen Sie es mit verschiedenen Kombinationen:

pax> ./prog 
Enter string>[CTRL-D] 
No input 

pax> ./prog 
Enter string> a 
OK [a] 

pax> ./prog 
Enter string> hello 
OK [hello] 

pax> ./prog 
Enter string> hello there 
Input too long [hello the] 

pax> ./prog 
Enter string> i am pax 
OK [i am pax] 

Was ich tun würde, ist zu verwenden, Diese Funktion, um eine Linie sicher zu bekommen, dann einfach verwenden:

sscanf (buffer, "%f %f", &real, &img) 

, um die tatsächlichen Werte zu erhalten (und die Anzahl zu überprüfen).


In der Tat, hier ist ein komplettes Programm, das näher ist, was Sie wollen:

#include <stdio.h> 
#include <string.h> 

#define OK  0 
#define NO_INPUT 1 
#define TOO_LONG 2 
static int getLine (char *prmpt, char *buff, size_t sz) { 
    int ch, extra; 

    // Get line with buffer overrun protection. 
    if (prmpt != NULL) { 
     printf ("%s", prmpt); 
     fflush (stdout); 
    } 
    if (fgets (buff, sz, stdin) == NULL) 
     return NO_INPUT; 

    // If it was too long, there'll be no newline. In that case, we flush 
    // to end of line so that excess doesn't affect the next call. 
    if (buff[strlen(buff)-1] != '\n') { 
     extra = 0; 
     while (((ch = getchar()) != '\n') && (ch != EOF)) 
      extra = 1; 
     return (extra == 1) ? TOO_LONG : OK; 
    } 

    // Otherwise remove newline and give string back to caller. 
    buff[strlen(buff)-1] = '\0'; 
    return OK; 
} 

 

int main (void) { 
    int i = 1, rc; 
    char prompt[50], buff[50]; 
    float real, imag; 

    while (1) { 
     sprintf (prompt, "\nEnter real and imaginary for #%3d: ", i); 
     rc = getLine (prompt, buff, sizeof(buff)); 
     if (rc == NO_INPUT) break; 
     if (*buff == '\0') break; 

     if (rc == TOO_LONG) { 
      printf ("** Input too long [%s]...\n", buff); 
     } 

     if (sscanf (buff, "%f %f", &real, &imag) == 2) { 
      printf ("Values were %f and %f\n", real, imag); 
      i++; 
     } else { 
      printf ("** Invalid input [%s]\n", buff); 
     } 
    } 

    return 0; 
} 

zusammen mit einem Testlauf:

pax> ./testprog 

Enter real and imaginary for # 1: hello 
** Invalid input [hello] 

Enter real and imaginary for # 1: hello there 
** Invalid input [hello there] 

Enter real and imaginary for # 1: 1 
** Invalid input [1] 

Enter real and imaginary for # 1: 1.23 4.56 
Values were 1.230000 and 4.560000 

Enter real and imaginary for # 2: 

pax> _ 
+0

+1 Wenn Sie 'scanf' verwenden, um Eingaben vom Benutzer zu lesen, treten häufig Probleme wie diese auf. Es ist normalerweise weniger fehleranfällig, die Eingabe einfach als eine Zeichenfolge zu lesen und dann die Zeichenfolge zu analysieren. – bta

2

Es gibt eine Möglichkeit, was zu tun wollen verwenden Sie nur scanf:

int readCoefficents(double complex *c) { 
    int i = 0; 
    double real; 
    double img; 
    char buf[2]; 
    while (scanf("%1[\n]", buf) == 0) {   // loop until a blank line or EOF 
     if (scanf("%lf %lf", &real, &img) == 2) // read two floats 
      c[i++] = real + img * I; 
     scanf("%*[^\n]");      // skip the rest of the line 
     scanf("%*1[\n]");      // and the newline 
    } 
    c[i++] = 1 + 0*I; // most significant coefficient is assumed to be 1 
    return i; 
} 

Wenn nur der Benutzer 1 Schwimmer auf einer Linie eintritt, wird es die nächste Zeile für den zweiten Wert zu lesen. Wenn ein zufälliger Müll eingegeben wird, springt er zu einem neuen Zeilensprung und versucht es erneut mit der nächsten Zeile. Andernfalls wird es fortfahren, Paare von Gleitkommawerten zu lesen, bis der Benutzer eine Leerzeile eingibt oder ein EOF erreicht wird.

0

re PAXDIABLO Lösung: es funktioniert nicht richtig mit Leerzeile vom Benutzer eingegeben, so dass diese Zeile wird in Ihrem getLine hinzugefügt werden() Funktion

if (strlen(buff) <= 1) return NO_INPUT; 

nach der Zeile:

if (fgets (buff, sz, stdin) == NULL) 
    return NO_INPUT; 

So wird es werden:

... 
    if (strlen(buff) <= 1) return NO_INPUT; 
    if (fgets (buff, sz, stdin) == NULL) return NO_INPUT; 
.... 
Verwandte Themen