web-dev-qa-db-ja.com

Cでユーザーから入力を取得する最良の方法はどれですか?

scanfと同じように、「より深刻なプログラム」ではgetlineを使用すべきではないと多くの人が言いました。

私は失われ始めました:人々に出会ったすべての入力機能が、それらのどれも使用すべきではないと言ったら、何を使うべきですか?知らない入力を取得するための、より「標準的な」方法はありますか?

38
user1115057

一般に、fgets()は適切なオプションと見なされます。行全体をバッファに読み込み、そこから必要なことを実行できます。 scanf()のような動作が必要な場合は、読んだ文字列をsscanf()に渡すことができます。

これの主な利点は、文字列の変換に失敗した場合、簡単に回復できることです。一方、scanf()を使用すると、stdinに入力する必要があるため、ドレインする必要があります。さらに、行指向の入力とscanf()を混在させる落とし穴に陥ることはありません。\nのようなものがstdinに残されたときに頭痛の種になります。完全に無視されます。

このような何かがあなたの好みに合うかもしれません:

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

上記のfgets()は、EOFまたはエラーのためにNULLを返すことに注意してください。これがifでラップした理由です。sscanf()呼び出しは、変換に成功しました。

行がバッファーよりも大きい場合、fgets()は行全体を読み取れない可能性があることに注意してください。これは、「深刻な」プログラムでは確かに考慮すべきものです。

35
FatalError

入力長に固定制限を設定できる単純な入力の場合、fgets()を使用して端末からデータを読み取ることをお勧めします。

これは、fgets()により、バッファサイズを指定できるためです(gets()とは異なり、この理由から、neverを使用して入力を読み取ります)人間):

_char line[256];

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

それが保持されることを忘れないでください驚くかもしれませんが、改行文字。

[〜#〜] update [〜#〜]:コメントで指摘したように、メモリを追跡する責任を得ることに問題がなければ、より良い代替手段があります:getline() 。これはおそらく、読み取られる行の長さに静的な制限がないため、POSIXコードの最適な汎用ソリューションです。

11
unwind

scanfの使用にはいくつかの問題があります。

  • プレーンな_%s_変換指定子を使用してテキストを読み取ることは、gets();を使用するのと同じリスクがあります。ユーザーがターゲットバッファーが保持するサイズよりも長い文字列を入力すると、バッファーオーバーランが発生します。

  • _%d_または_%f_を使用して数値入力を読み取る場合、特定の不正なパターンを完全にキャッチおよび拒否することはできません-_%d_で整数を読み取り、ユーザーが「_12r4_ "、scanfは、入力ストリームに_12_を残して_r4_を変換して割り当て、次の読み取りをファウルします。

  • 一部の変換指定子は先頭の空白をスキップしますが、他の変換指定子はそうしません。これを考慮しないと、一部の入力が完全にスキップされるという問題が発生する可能性があります。

基本的に、scanfを使用して防弾読み取りを行うには、余分な労力のlotが必要です。

適切な代替方法は、fgets()を使用してすべての入力をテキストとして読み取り、sscanfまたはstrtokstrtolの組み合わせを使用して入力をトークン化および変換することです。 strtodなど.

5
John Bode

fgetsを使用してデータを取得し、sscanf(または別のメソッド)を使用してそれらを解釈します。

fgetsよりもsscanf + scanfを使用するほうが良い理由については、このページを参照してください。

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

2
ouah