2016-08-22 5 views
2

Прежде всего, спасибо вам за помощь!Остановите цикл for, когда пользователь закончил ввод ввода в c

Я новичок в языке C (и вообще программировании), и я пытаюсь написать программу, в которой пользователь вводит данные. Затем точки данных сохраняются в массиве, где их можно манипулировать.

Где я застреваю: я хочу, чтобы пользователь мог вводить (почти) любое количество точек, а затем использовать «ключевое слово» видов, чтобы сигнализировать о завершении ввода данных. В этом случае пользователь набирает «done».

Вот что я до сих пор:

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

int main(void) { 

printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n"); 

double data[1048]; 
int i, count; 

for (i = 1; ;i++) { 

    printf("Data[%i]: ", i); 
    scanf("%lf", &data[i]); 

    if (data[i] == 'done') { 

     break; 

    } else { 

     count++; 

    } 

    } 

} 

Я попытался 'возвращение 1;' и «перерыв». Каждый раз, когда программа работает хорошо, пока «ключевое слово» вводятся, в какой момент я получаю:

Data[8]: Data[9]: ... Data[1120]: Data[1Segmentation fault 11 

Единственный раз, когда он работает, если он у меня перерыв, когда пользователь вводит определенное число (например, -1 или 0). Но это не работает для пользователя, поскольку они могут вводить эти числа в виде точек данных.

Извините за длинный пост, но я ценю помощь!

+1

Тип данных для данных - двойные двойные данные [1048]; и вы приравниваете его к значениям char. Это ошибка типа. Если вы хотите приравнивать, используйте длинный, а не двойной. Двойной тип с плавающей точкой. – xCodeZone

+1

Okey, okey. Как это было скомпилировано? – Inline

+0

'' 'для значений типа' char', который представляет собой только один символ. Не имеет смысла писать '' done'', это строка. – Barmar

ответ

4

Вы получили ряд хороших ответов на свой вопрос, и есть еще несколько способов сделать ввод doubles и остановиться на "done". Так как вы изучаете C, всегда, ВСЕГДА (в случае, если это не было ясно), проверки возвращениеscanf для проверки количества конверсий вы ожидали на самом деле имели место. [1] (это также ваш путь для завершения ввода на "done" (или любой недвоеные Введенный вызывая scanf вернуть менее 1)

Как отмечено в комментарии, массивы с нуля в C. Когда вы принимаете ввод, вы хотите использовать count как ваш индекс массива, а не i (в этом случае, если вы выходите из чтения при каждом сбое - это не имеет значения, но вы можете так же легко снова запросить дополнительный ввод и приращение count только при успешном возврате с scanf) Назад к вашим запросам Тион. Если вы сконфигурируете цикл чтения для непрерывного цикла до тех пор, пока не произойдет сбой scanf, вы можете использовать временную переменную для первоначального захвата входного значения и назначить значение только вашему массиву и привить индекс к успеху. например (С постоянной MAXD = 1048)

for (;;) { /* loop until scanf input fails (with 'done') */ 

    double tmp; /* block scope declarations are fine */ 

    printf (" data[%4d]: ", count); 

    if (count < MAXD && scanf(" %lf", &tmp) == 1) 
     data[count++] = tmp; 
    else 
     break; 
} 

(вы можете даже переместить копию строки выше цикла, и переместить одну выше после if (....) {...} устранить подсказку, когда предел массива (MAXD) достигается - это осталось как упражнение)

В приведенном выше примере у вас есть 2 условия, которые вы выполняете перед сохранением значения.(1) вы ограничиваете количество значений, которое может хранить ваш пользователь до MAXD, и (2) вы сохраняете только значение, если действительное преобразование в double происходит в scanf. Вы покидаете цикл, если какое-либо из условий не работает (что, если вы введете "done" в качестве двойного значения, оно будет).

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

#include <stdio.h> 

enum { MAXD = 1048 }; /* declare constants instead of using magic numbers */ 

int main (void) { 

    double data[MAXD] = {0}; /* in ISO C declarations come before code */ 
    int i, count = 0;   /* initializing variable saves debug time */ 

    printf ("\n Welcome! \n\n Please enter each data point. " 
      "Enter 'done' when finished.\n\n"); 

    for (;;) { /* loop until scanf input fails (with 'done') */ 

     double tmp; /* block scope declarations are fine */ 

     printf (" data[%4d]: ", count); 

     if (count < MAXD && scanf(" %lf", &tmp) == 1) 
      data[count++] = tmp; 
     else 
      break; 
    } 

    printf ("\n %d values entered:\n\n", count); 

    for (i = 0; i < count; i++) 
     printf (" data[%4d] : %.2lf\n", i, data[i]); 

    return 0; /* main() is type 'int' and returns a value */ 
} 

Пример использования/вывода

$ ./bin/scanfdoubles 

Welcome! 

Please enter each data point. Enter 'done' when finished. 

data[ 0]: 1.1 
data[ 1]: 1.2 
data[ 2]: 1.3 
data[ 3]: 1.4 
data[ 4]: 1.5 
data[ 5]: 1.6 
data[ 6]: done 

6 values entered: 

    data[ 0] : 1.10 
    data[ 1] : 1.20 
    data[ 2] : 1.30 
    data[ 3] : 1.40 
    data[ 4] : 1.50 
    data[ 5] : 1.60 

Посмотрите вещи над и дайте мне знать, если у вас есть какие-либо вопросы.

сносок:

1. в то время как вы можете использовать scanf принять ввод пользователя в C, вы лучше использовать строковый функцию (например, fgets), а затем разбор всей строки (например, sscanf). Это позволяет вам (1) проверять чтение (например, возврат fgets), а затем (2) отдельно проверять значение, введенное пользователем. Это развязка вашего чтения, и ваш синтаксический анализ имеет много преимуществ.

+0

Ничего себе! Отличный ответ. Большое спасибо! В настоящее время я прохожу через _Programming в C, 4th ed._ от Stephen Kochan, и все, что он действительно охватил до сих пор, это «scanf», поэтому мне нужно будет провести еще несколько исследований по технике (-ям), которую вы упомянули в ваша сноска. И просто перефразируем, поэтому я понимаю: 'scanf' возвращает' 0', если не удалось, '1' в случае успеха.Таким образом, я могу проверить успех, проверив «1» [как в 'scanf ("% lf ", & tmp) == 1']. Если '0', мой цикл останавливается. – chadathin

+0

Нет, все функции 'scanf' (' scanf', 'sscanf' и т. Д.) Возвращают счетчик *** ***, который является числом * успешных конверсий *, которые происходят в соответствии с * ** спецификаторы формата ***, содержащиеся в строке *** ***. (например, * format-string * '"% lf "' содержит один * спецификатор формата * '% lf' для преобразования в' double'. Поэтому возврат '1' указывает на успех, что-либо еще, и' EOF' (как правило, '-1') будет означать, что конец файла достигнут. Рад, что ответ помог, C - фантастический язык, требуется время, чтобы учиться, поэтому замедлить, наслаждаться поездкой. –

+0

В рамках учебного процесса, одним из неоценимых инструментов являются * man-страницы *. Они объясняют каждую функцию в виду ошеломляющей детали. Они выглядят загадочными, когда вы сначала смотрите на них, но делайте, и найдите время, чтобы позволить тому, что они говорят, что вы погружаетесь. в то время как вторая натура должна отменить «man scanf» (или получить страницу в Интернете) и получить всю необходимую информацию с первого взгляда. –

0
  • Ваш data имеет тип double. Он не может сканировать буквальное «сделано».

Вместо этого используйте EOF для проверки конца ввода.

while(scanf("%lf",&data[i]) != EOF) { 
    ... 
} 

Другой способ:

while(scanf("%lf",&data[i]) == 1) { 
    ... 
} 
  • Другое дело, инициализировать счетчик до нуля, т.е. count = 0;
+0

Первый способ плохой, он не будет определять, «done» –

+0

Если это даже был строковый литерал '' done '', то он стоит его многобайтовый символ' 'done''. – alk

+0

Вот почему я предложил использовать EOF вместо использования литералов для проверки конца ввода. – Shahid

-1

Тип данных для данных двойного

double data[1048]; 

, и вы приравниваете его к символьным значениям. Это ошибка типа. Если вы хотите приравнивать, используйте тип символьных данных, а не double. Двойной тип с плавающей точкой.

1

Ни один элемент данных [] никогда не будет «выполнен» (они будут плавать). Если вы хотите напрямую сканировать(), вам нужно выбрать двойное значение, которое завершает последовательность (обычно ноль или -1 или что-то еще). Если это не будет работать, вы можете использовать что-то вроде:

  1. Используйте fgets(), чтобы вытащить строку, а затем strncmp(), чтобы проверить значение завершающего и sscanf(), чтобы вытащить двойной или :
  2. Попросите пользователя использовать Ctrl-D для завершения и проверить значение проверки для EOF.

О, и, строго говоря, у вас есть верхний предел записей. Вы должны проверить i, чтобы убедиться, что вы не превышаете это. Никогда не предполагайте, что ваш вход не будет превышать границы. sizeof() для статически назначенной переменной или некоторого #defined макроса для отслеживания этого.

+1

'gets()' устарел, используйте 'fgets()'. – Barmar

+0

Слишком право. Я долгое время программировал на C :-). Ответ обновлен. –

0

Нижняя линия: не использовать scanf.

использовать что-то вроде

char inputline[100]; 

i = 0; 

while(fgets(inputline, sizeof(inputline), stdin) != NULL) { 
    if(strncmp(inputline, "done", 4) == 0) break; 
    data[i++] = atof(inputline); 
} 

scanf достаточно трудно использовать даже тогда, когда все ваши входы цифры, которые вы ожидаете. Если вход может быть либо числом, либо словом «сделано», scanf никогда не будет работать. Но чтение строки текста, как здесь, обычно проще и гибче.

P.S. Вам также нужно беспокоиться о том, что пользователь вводит более 1048 номеров.

+0

Что вы думаете о ситуации, когда символы 'sizeof (inputline) - 1' считываются из' stdin', ни одна из них не является символом новой строки, и поэтому 'fgets' больше не читает, оставляя' stdin' в несогласованном состоянии для следующего циклическая итерация? – Sebivor

+0

Кроме того, вы пишете * «Если ввод может быть либо числом, либо словом« сделано »,« scanf »никогда не будет работать». *, Но я думаю, что он может и работает здесь, в этом самом вопросе. Замените логику с замкнутым циклом OPs, например: 'if (scanf ("% lf ", & data [i]) == 1) {count ++; Продолжать; } char e; if (feof (stdin) || ferror (stdin) || (scanf («don% c», & e) == 1 && e == 'e')) {break; } '... Я оставлю это как вызов для вас придумать что-то столь изящно выраженное и все же надежное без использования' scanf'. – Sebivor

0

Для вашей задачи цикл сбора ввода должен контролировать не только ключевое слово, но и число, если вводится. Я предлагаю сделать это следующим образом:

#include <stdio.h> 
#include <string.h> 
#define NUM_OF_DATA 1048 

int main(void) 
{ 
    printf("\n Welcome! \n\n Please enter each data point. Enter 'done' when finished.\n\n"); 
    double data[NUM_OF_DATA]; 
    int i; // counter of entered numbers 
    char str[5] = { 0 }; // string to read 'done' or other word from input 
    for (i = 0; i < NUM_OF_DATA; i++) // the first index of data in array is 0 (the last NUM_OF_DATA-1) 
    { 
     printf("Data[%i]: ", i); 
     if (1 == scanf("%lf", &data[i])) // if number was successfully read 
      continue; // go to next iteration 
     // if some problem was with reading a loat number 
     // read the string 
     scanf("%4s", str); // read not more than 4 characters from input 
     if (strcmp(str, "done") == 0) 
     { 
      break; // stop input if 'done' was entered 
     } 
     // clean input buffer before next input 
     while (getchar() != '\n'); 
     // correct counter in case of wrong input 
     i--; 
    } 
    // output the number of correct inputs 
    printf("%d numbers were entered.\n", i); 
    // do something with data 
    // taking in account, that i is not index of the last element, 
    // but the number of elements (indexes are 0 ... i-1) 
    // ... 
    return 0; 
} 

Этот for цикл останавливается в двух случаях:

1) когда data массив заполнен,

2), когда 'done' без кавычек вошел.

Дополнительная функция - пропустить неправильный ввод (попробуйте, как это работает).