Jaki jest najlepszy sposób na uzyskanie informacji od użytkownika w C?

Wiele osób mówiło, że scanf nie powinno być używane w "poważniejszym programie", tak samo jak w getline.

Zacząłem się gubić: jeśli każda funkcja wejściowa, którą napotkałem, mówi, że nie powinienem używać żadnej z nich, to czego powinienem użyć? Czy istnieje bardziej "standardowy" sposób uzyskania danych wejściowych, którego nie jestem świadomy?

Author: nbro, 2012-02-14

4 answers

Ogólnie rzecz biorąc, fgets() jest uważany za dobrą opcję. Czyta całe linie do bufora, a stamtąd możesz zrobić to, czego potrzebujesz. Jeśli chcesz zachowywać się jak scanf(), możesz przekazać ciągi, które czytasz do sscanf().

Główną zaletą tego jest to, że jeśli łańcuch nie zostanie przekonwertowany, łatwo go odzyskać, podczas gdy z scanf() zostaje Ci wejście na stdin, które musisz opróżnić. Dodatkowo nie znajdziesz się w pułapce mieszania danych wejściowych zorientowanych liniowo z scanf(), co powoduje bóle głowy kiedy rzeczy takie jak \n zostają włączone stdin, co powoduje, że nowi koderzy sądzą, że wywołania wejściowe zostały całkowicie zignorowane.

Coś takiego może Ci się spodobać:

char line[256];
int i;
if (fgets(line, sizeof(line), stdin)) {
    if (1 == sscanf(line, "%d", &i)) {
        /* i can be safely used */
    }
}

Powyżej należy zauważyć, że fgets() zwraca NULL na EOF lub error, dlatego zawinąłem go w if. Wywołanie sscanf() Zwraca liczbę pól, które zostały pomyślnie przekonwertowane.

Należy pamiętać, że fgets() może nie odczytać całej linii, jeśli linia jest większa niż bufor, który w "poważny" program jest z pewnością coś, co należy rozważyć.

 35
Author: FatalError,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 14:11:06

Dla prostego wejścia, gdzie można ustawić stałe ograniczenie długości wejścia, polecam odczyt danych z terminala za pomocą fgets().

Dzieje się tak dlatego, że fgets() pozwala określić rozmiar bufora (w przeciwieństwie do gets(), który z tego właśnie powodu powinien w zasadzie nigdy {[9] } być używany do odczytu danych od ludzi):

char line[256];

if(fgets(line, sizeof line, stdin) != NULL)
{
  /* Now inspect and further parse the string in line. */
}

Pamiętaj, że zachowa np. znak (- y) liniowy (- e), co może być zaskakujące.

UPDATE: jak zaznaczono w komentarzu, jest lepsza alternatywa, jeśli nie masz nic przeciwko odpowiedzialności za śledzenie pamięci: getline(). Jest to prawdopodobnie najlepsze rozwiązanie ogólnego przeznaczenia dla kodu POSIX, ponieważ nie ma statycznego limitu długości linii do odczytu.

 11
Author: unwind,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 14:29:48

Istnieje kilka problemów z użyciem scanf:

  • Odczyt tekstu za pomocą zwykłego specyfikatora Konwersji %s wiąże się z takim samym ryzykiem jak użycie gets(); jeśli użytkownik wpisuje ciąg znaków, który jest dłuższy niż rozmiar przechowywanego bufora docelowego, zostanie przekroczony bufor;

  • Jeśli używasz %d lub %f do odczytu danych liczbowych, pewne złe wzorce nie mogą zostać przechwycone i odrzucone całkowicie -- jeśli czytasz liczbę całkowitą z %d i typami użytkowników"12r4", scanf przekonwertuje i przypisze 12 pozostawiając r4 w strumieniu wejściowym do następnego odczytu;

  • Niektóre specyfikatory konwersji pomijają wiodące białe znaki, inne nie, a nieprzestrzeganie tego może prowadzić do problemów, w których niektóre dane wejściowe są pomijane całkowicie;

W Zasadzie, to wymaga dużo wysiłku , aby kuloodporne odczyty za pomocą scanf.

Dobrą alternatywą jest odczytanie wszystkich danych wejściowych jako tekstu za pomocą fgets(), a następnie tokenizowanie i przekonwertować dane wejściowe za pomocą sscanf lub kombinacji strtok, strtol, strtod, itd.

 5
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 14:19:00

Użyj fgets, aby uzyskać Dane i użyj sscanf (lub innej metody), aby je zinterpretować.

Zobacz tę stronę, aby dowiedzieć się, dlaczego lepiej jest używać fgets + sscanf zamiast scanf

Http://c-faq.com/stdio/scanfprobs.html

 2
Author: ouah,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 14:12:58