web-dev-qa-db-ja.com

scanf( "%[^ \ n] s"、a)とgets(a)

ユーザーが文字列を入力するときにscanfを使用しないように言われました。代わりに、ほとんどのエキスパートとStackOverflowのユーザーによるgets()を探してください。 StackOverflowで、文字列にscanf overgetを使用しない理由を尋ねたことはありません。これは実際の質問ではありませんが、この質問への回答をいただければ幸いです。

今、実際の質問に来ています。私はこのタイプのコードに出くわしました-

scanf("%[^\n]s",a); 

これは、ユーザーが新しい行文字を入力するまで文字列を読み取り、空白も文字列と見なします。

使っても問題ありませんか

scanf("%[^\n]s",a);

取得する代わりに?

見た目はscanf関数よりも最適化されており、getsは純粋に文字列の処理専用です。これについて教えてください。

更新

This リンクは私がそれをよりよく理解するのを助けました。

9
niko

gets(3)は危険であり、絶対に避けてください。 gets(3)notセキュリティ上の欠陥である場合の使用は想像できません。

scanf(3)%sも危険です。割り当てたバッファのサイズを示すには、「フィールド幅」指定子を使用する必要があります。フィールド幅がないと、このルーチンはgets(3)と同じくらい危険です。

char name[64];
scanf("%63s", name);

GNU Cライブラリはamodifier to %sを提供し、バッファを割り当てます。この移植性のない拡張機能はおそらく少ないでしょう。正しく使用するのが難しい:

   The GNU C library supports a nonstandard extension that
   causes the library to dynamically allocate a string of
   sufficient size for input strings for the %s and %a[range]
   conversion specifiers.  To make use of this feature, specify
   a as a length modifier (thus %as or %a[range]).  The caller
   must free(3) the returned string, as in the following
   example:

       char *p;
       int n;

       errno = 0;
       n = scanf("%a[a-z]", &p);
       if (n == 1) {
           printf("read: %s\n", p);
           free(p);
       } else if (errno != 0) {
           perror("scanf");
       } else {
           fprintf(stderr, "No matching characters\n"):
       }

   As shown in the above example, it is only necessary to call
   free(3) if the scanf() call successfully read a string.
7
sarnold

まず、sがフォーマット文字列で何をしているのかが明確ではありません。 _%[^\n]_の部分は、自給自足のフォーマット指定子です。ご存知のように、これは_%s_形式の修飾子ではありません。これは、_"%[^\n]s"_フォーマット文字列がscanfによって2つの独立したフォーマット指定子として解釈されることを意味します:_%[^\n]_の後に1つのsが続きます。これにより、scanfは、_\n_が検出されるまで(_\n_は未読のまま)すべてを読み取るように指示され、次の入力文字はsである必要があります。これはまったく意味がありません。このような自己矛盾する形式に一致する入力はありません。

第二に、明らかに意味されたのはscanf("%[^\n]", a)です。これは[もう利用できません] gets(またはfgets)にやや近いですが、同じではありません。 scanfでは、各形式指定子が少なくとも1つの入力文字と一致する必要があります。 scanfは失敗し、要求されたフォーマット指定子の入力文字と一致しない場合は中止されます。これは、scanf("%[^\n]",a)が空の入力行、つまり_\n_文字を含む行をすぐに読み取ることができないことを意味します。このような行を上記のscanfにフィードすると、失敗を示すために_0_が返され、aは変更されません。これは、一般的なラインベースの入力関数の動作とは大きく異なります。

(これは、_%[]_形式のかなり驚くべき、一見非論理的です。個人的には、_%[]_が空のシーケンスに一致し、空の文字列を生成できるようにしたいと思いますが、それは標準的なscanf動作します。)

入力を行ごとに読み取りたい場合は、fgetsが最適なオプションです。

4
AnT