この回答では、あなたがテキストの行を読んで解釈していると仮定します。おそらく、何かを入力してRETURNを押しているユーザーにプロンプトを表示しているのでしょう。あるいは、何らかのデータファイルから構造化テキストの行を読み取っているのかもしれません。
テキストの行を読み取っているので、テキストの行を読み取るライブラリ関数を中心にコードを編成することは理にかなっています。標準関数はですがfgets()
、他にもあります(などgetline
)。次に、そのテキスト行をどうにかして解釈します。
fgets
テキストの行を読み取るために呼び出すための基本的なレシピは次のとおりです。
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
これは単に1行のテキストを読み取り、それを印刷します。書かれているように、これにはいくつかの制限があります。これについては、すぐに説明します。また、これには非常に優れた機能があります。2番目の引数として渡した512という数値fgets
は、読み込む配列のサイズ
line
ですfgets
。この事実- fgets
読み取りが許可されている量を示すことができる-はfgets
、配列に過度に読み取ることで配列がオーバーフローしないことを確認できることを意味します。
これで、テキストの行を読み取る方法がわかりましたが、整数、浮動小数点数、単一の文字、または単一の単語を本当に読み取りたい場合はどうでしょうか。(どのような場合にはつまり、
scanf
私たちが改善しようとしているコールは次のように書式指定子を使用していた%d
、%f
、%c
、または%s
?)
テキスト行(文字列)をこれらのいずれかのように再解釈するのは簡単です。文字列を整数に変換するための最も簡単な(不完全ではある)方法は、を呼び出すことatoi()
です。浮動小数点数に変換するには、がありatof()
ます。(そして、すぐにわかるように、より良い方法もあります。)以下に、非常に簡単な例を示します。
printf("type an integer:\n");
fgets(line, 512, stdin);
int i = atoi(line);
printf("type a floating-point number:\n");
fgets(line, 512, stdin);
float f = atof(line);
printf("you typed %d and %f\n", i, f);
ユーザーに単一の文字を入力させたい場合(おそらく、y
または
n
yes / noの応答として)、次のように文字通り行の最初の文字を取得できます。
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(もちろん、これはユーザーが複数文字の応答を入力した可能性を無視します。入力された余分な文字を静かに無視します。)
最後に、ユーザーに空白を絶対に含まない文字列を入力させたい場合、入力行を処理したい場合
hello world!
文字列の"hello"
後に別の何かが続くので(これはscanf
フォーマット%s
が行うことになったことです)、まあ、その場合、私は少し手を振ったので、結局そのように行を再解釈することはそれほど簡単ではないので、その答え質問の一部は少し待つ必要があります。
しかし、最初に、スキップした3つの点に戻りたいと思います。
(1)ずっと電話してきた
fgets(line, 512, stdin);
配列に読み込むline
場合、512は配列のサイズなline
のでfgets
、オーバーフローしないことがわかります。ただし、512が正しい数であることを確認するには(特に、プログラムを調整してサイズを変更した可能性があるかどうかを確認するため)、line
宣言された場所まで読み戻す必要があります。これは厄介なことなので、サイズを同期させるには、2つの優れた方法があります。(a)プリプロセッサを使用してサイズの名前を作成できます。
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
または、(b)Cのsizeof
演算子を使用します。
fgets(line, sizeof(line), stdin);
(2)2番目の問題は、エラーをチェックしていないことです。入力を読み取るときは、常にエラーの可能性を確認する必要があります。何らかの理由でfgets
、要求したテキストの行を読み取れない場合は、nullポインターを返すことでこれを示します。だから私たちは次のようなことをしていたはずです
printf("type something:\n");
if(fgets(line, 512, stdin) == NULL) {
printf("Well, never mind, then.\n");
exit(1);
}
最後に、テキストの行を読み取るために、という問題があります
fgets
文字を読み取り、それを見つけるまで、あなたの配列にそれらを満たす\n
回線を終端文字を、それがいっぱい\n
すぎて、あなたの配列に文字を。これは、前の例を少し変更すると確認できます。
printf("you typed: \"%s\"\n", line);
これを実行し、プロンプトが表示されたときに「Steve」と入力すると、出力されます
you typed: "Steve
"
それ"
それが戻っ読み出して印刷された文字列が実際にあったので、二行目にあります"Steve\n"
。
余分な改行は問題にならない場合があります(atoi
またはを呼び出したときのように
atof
、どちらも数値の後の余分な非数値入力を無視するため)。そのため、多くの場合、その改行を削除します。その方法はいくつかありますが、これについては1分後に説明します。(私は多くのことを言ってきたことを知っています。しかし、私はそれらすべてのものに戻ると約束します。)
この時点で、あなたは考えているかもしれません:「あなたはscanf
悪いことだと思っていたので、これfgets
はもっと良い方法だと思いました。しかし、迷惑のように見え始めています。通話scanf
はとても簡単だったのです!使い続けることはできませんか? 」
もちろん、必要scanf
に応じてを使い続けることができます。(そして、本当に
単純なことについては、いくつかの点でより単純です。)しかし、17の癖とfoiblesの1つが原因で失敗した場合、または入力のために無限ループに陥った場合、私に泣いてはいけません期待していなかった、またはそれを使用してより複雑な何かを行う方法を理解できない場合。そして、のfgets
実際の迷惑を見てみましょう:
常に配列サイズを指定する必要があります。もちろん、それはまったく厄介なことではありません。これは機能です。バッファオーバーフローは本当に悪いことです。
戻り値を確認する必要があります。実際、これはウォッシュscanf
です。正しく使用するには、戻り値もチェックする必要があるためです。
あなたは\n
背中をはぎ取らなければならない。これは本当の迷惑です。この小さな問題がなかったことを指摘できる標準関数があればいいのにと思います。(誰も育てないでくださいgets
。)しかし、scanf's
17種類の迷惑と比較して、私はこの1つの迷惑をfgets
いつの日か受けます。
では、どのようにしてその改行を取り除くのですか?3つの方法:
(a)明白な方法:
char *p = strchr(line, '\n');
if(p != NULL) *p = '\0';
(b)トリッキーでコンパクトな方法:
strtok(line, "\n");
残念ながら、これは常に機能するとは限りません。
(c)別のコンパクトでややあいまいな方法:
line[strcspn(line, "\n")] = '\0';
そして今、それが邪魔にならないようだと、我々は戻って、私はスキップ別のものに得ることができますの不完全性atoi()
とatof()
。それらの問題は、成功または失敗の成功を示す有用な指標を提供しないことです。これらは、後続の非数値入力を静かに無視し、数値入力がまったくない場合は静かに0を返します。他にもいくつかの利点がある好ましい代替案はstrtol
、とstrtod
です。
strtol
また、あなたは(とりわけ)の効果を得ることができることを意味し、10以外のベースを使用することができます%o
か、%x
とscanf
。しかし、これらの関数を正しく使用する方法を示すこと自体がストーリーであり、すでにかなり断片化された物語になっているものから気を散らすので、ここではこれ以上何も述べません。
残りの主なナラティブは、解析しようとしている可能性のある入力に関するもので、1つの数字や文字だけではなく、より複雑です。2つの数字、空白で区切られた複数の単語、または特定のフレーミング句読点を含む行を読みたい場合はどうでしょうか。ここで物事が面白くなり、を使用してやろうとすると物事がおそらく複雑になり、を使用してscanf
1行のテキストをきれいに読んだので、非常に多くのオプションがありfgets
ます。おそらく本を埋めることができるので、ここでは表面のみをスクラッチすることができます。
私のお気に入りのテクニックは、行を空白で区切られた「単語」に分割し、各「単語」でさらに何かを行うことです。これを行うための1つの主要な標準機能は
strtok
(これにも問題があり、全体の個別の議論を評価する)です。私自身の好みは、分割された各「単語」へのポインタの配列を構築するための専用関数であり、これらのコースノートで説明し
ます。いずれにせよ、「単語」を取得したら、おそらく
すでに見てきた同じatoi
/ atof
/ strtol
/ strtod
関数を使用して、それぞれをさらに処理できます。
逆説的に言えば、ここから離れる方法を見つけるためにかなりの時間と労力を費やしてきましたが、scanf
今読んだテキストの行を処理するもう1つの優れた方法
fgets
は、に渡すことsscanf
です。このようにして、のほとんどの利点が得られますscanf
が、ほとんどの欠点はありません。
入力構文が特に複雑な場合は、「regexp」ライブラリを使用して解析するのが適切な場合があります。
最後に、必要に応じてアドホック解析ソリューションを使用できます。char *
期待する文字をチェックするポインタを使用して、一度に1行ずつ行を移動でき
ます。それとも、好きな機能を使用して、特定の文字を検索することができるstrchr
かstrrchr
、またはstrspn
あるいはstrcspn
、またはstrpbrk
。または、前にスキップしたstrtol
or
strtod
関数を使用して、数字文字のグループを解析/変換してスキップできます。
言うことができることはもっとたくさんありますが、うまくいけば、この紹介があなたを始めさせるでしょう。
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
は、末尾の非数値テキストを不良として検出しないことに注意してください。