2010-09-16 2 views
6

Если я нахожу 5 5 на терминале, нажмите enter и нажмите Enter еще раз, я хочу выйти из цикла.Получить scanf для выхода, когда он читает новую строку?

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

Очевидно, что код не делает работу для меня (да, я знаю, что есть переполнение буфера для того чтобы случиться).

scanf не будет уходить, если я не нахожу букву (или некоторую нечисловую, а не простую строку). Как заставить scanf завершить работу после чтения пустой строки?

+0

Если вам нужно больше причин, чтобы избежать использования 'scanf': http://c-faq.com/stdio/scanfprobs.html – jamesdlin

ответ

7

Используйте fgets для чтения консольного ввода:

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

Предоставление вам кредита с тех пор, как вы упомянули fgets и sscanf. – Matt

+0

Спасибо, только что понятый c, вероятно, не позволит вам объявить буфер внутри цикла while, но концепция есть. – MattSmith

+2

Эта декларация в порядке. – jamesdlin

9

Конкретная проблема у Вас есть то, что формат scanf строка %f будет пропускать пробелы (включая строки) до тех пор, пока не найдет фактический характер сканирования. От стандарта C99:

Спецификация преобразования выполняется в следующих шагов:
      -       ввода пробельные символы (как определено с помощью функции isspace) пропускаются, если спецификация не включает в себя a '[', 'c', или 'n' спецификатор.

и, в другом месте, описывая isspace():

Стандартные пробельные символы являются: пространство ' ', форма подачи '\f', новая линия '\n', возврат каретки '\r', горизонтальная вкладка '\t', и вертикальная вкладка .

Лучше всего использовать fgets, чтобы получить линию (и это может быть защищен от переполнения буфера очень легко), а затем использовать sscanf на результирующей линии.

Функция scanf является одной из тех, на которые вы должны смотреть очень осторожно. Следующий фрагмент кода один я часто использую для обработки ввода строки:

#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; 
} 

тестирования с различными комбинациями:

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] 

Что бы сделать, это использовать эту функцию, чтобы получить линию безопасно, а затем просто используйте:

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

, чтобы получить фактические значения (и проверить количество).


В самом деле, вот полная программа, которая находится ближе к тому, что вы хотите:

#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; 
} 

вместе с теста:

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 Использование 'scanf' для чтения ввода от пользователя часто вводит такие проблемы. Как правило, гораздо меньше подвержено ошибкам просто читать ввод как строку, а затем анализировать строку. – bta

2

Есть способ сделать то, что Вы хотите использовать только зсапЕ:

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

Если пользователь вводит только 1 поплавок на линии, он будет читать следующую строку для второго значения. Если какой-либо случайный мусор введен, он перейдет к новой строке и повторит попытку со следующей строкой. В противном случае он просто начнет считывать пары значений float до тех пор, пока пользователь не войдет в пустую строку или не достигнет EOF.

0

повторно PAXDIABLO решение: он не работает должным образом с пустой строкой, введенной пользователем, поэтому эта строка должна быть добавлена ​​в вашей GetLine функция

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

() после строки:

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

Таким образом, это станет следующим:

... 
    if (strlen(buff) <= 1) return NO_INPUT; 
    if (fgets (buff, sz, stdin) == NULL) return NO_INPUT; 
.... 
Смежные вопросы