scanfを使用してスペースを入力するにはどうすればよいですか?


129

次のコードを使用します。

char *name = malloc(sizeof(char) + 256); 

printf("What is your name? ");
scanf("%s", name);

printf("Hello %s. Nice to meet you.\n", name);

ユーザーは、自分の名前を入力することができますが、彼らは次のようにスペースで名前を入力するとLucas Aardvarkscanf()直後にすべてを遮断しますLucasscanf()スペースを許可する方法


9
より慣用的であることに注意してください 'malloc(sizeof(char)* 256 + 1)'、または 'malloc(256 + 1)'、またはそれ以上( 'name'はローカルで厳密に使用されると想定) 'char name [256 + 1 ] '。「+1」は、ヌルターミネータのニーモニックとして機能することができ、割り当てに含める必要があります。
バリーケリー

@バリー-私sizeof(char) + 256はタイプミスだったと思います。
Chris Lutz

回答:


186

人々 (および特に初心者)は使用しないでくださいscanf("%s")gets()、またはあなたが入力が常に特定のフォーマット(そしておそらくないにも当時)のものであろうことを確かに知っている場合を除き、バッファオーバーフロー保護機能を持っていない任意の他の機能。

scanfは「スキャン形式」を意味することを忘れないでください。ユーザーが入力したデータよりも、形式がそれほど重要ではありませ。入力データ形式を完全に制御できれば理想的ですが、一般にユーザー入力には適していません。

入力を文字列に入れて評価するためにfgets()(バッファオーバーフロー保護を備えています)を使用しますsscanf()。ユーザーが解析せずに入力したものだけが必要なのでsscanf()、この場合はとにかく本当に必要はありません。

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

/* Maximum name size + 1. */

#define MAX_NAME_SZ 256

int main(int argC, char *argV[]) {
    /* Allocate memory and check if okay. */

    char *name = malloc(MAX_NAME_SZ);
    if (name == NULL) {
        printf("No memory\n");
        return 1;
    }

    /* Ask user for name. */

    printf("What is your name? ");

    /* Get the name, with size limit. */

    fgets(name, MAX_NAME_SZ, stdin);

    /* Remove trailing newline, if there. */

    if ((strlen(name) > 0) && (name[strlen (name) - 1] == '\n'))
        name[strlen (name) - 1] = '\0';

    /* Say hello. */

    printf("Hello %s. Nice to meet you.\n", name);

    /* Free memory and exit. */

    free (name);
    return 0;
}

1
知りませんでしたfgets()。それは実際にそれを使用する方が簡単に見えますscanf()。+1
クレデンス2009

7
ユーザーから連絡を取りたいだけなら、簡単です。また、バッファオーバーフローを回避できるため、安全です。scanfファミリは、文字列を別のものに変換するのに非常に便利です(たとえば、「%c%c%c%c%d」を使用した4つの文字とintのような)、それでも、fgetsとsscanfを使用する必要があります。 scanf。バッファオーバーフローの可能性を回避します。
paxdiablo 2009

4
最大バッファーサイズをscanf形式で配置できますが、実行時にフォーマットを構築せずに実行時に計算されたものを配置することはできません(printfの*に相当するものはありません。*は別の動作のscanfの有効な修飾子です:割り当ての抑制)。
AProgrammer 2009

注意また、そのscanf数値の変換がオーバーフローした場合に(未定義の動作を持っているN1570 7.21.6.2p10、最後の文は、言葉遣いC89以来変わらず)を意味しないscanf機能は、安全、信頼できない入力の数値変換のために使用することができます。
zwol

@JonathanKomarおよび将来これを読む他の人:教授があなたがscanf課題で使用する必要があると言った場合、彼らはそうするのが間違っていたので、私がそう言ったと彼らに言って、彼らがそれについて私と議論したい場合は、私のメールアドレスは私のプロフィールから簡単に見つけることができます。
zwol

124

試す

char str[11];
scanf("%10[0-9a-zA-Z ]", str);

お役に立てば幸いです。


10
(1)スペースを受け入れるには明らかに、文字クラスにスペースを入れる必要があります。(2)10は読み取られる文字の最大数であるため、strは少なくともサイズ11のバッファを指す必要があることに注意してください。(3)ここの最後のsはフォーマットディレクティブではありませんが、scanfはここに正確に一致するように試みます。効果は1234567890のようなエントリに表示され、sは消費されますが、どこにも配置されません。他の手紙は消費されません。sの後に別のフォーマットを配置すると、一致するsがある場合にのみ読み取られます。
AProgrammer 2009

もう1つの潜在的な問題として、最初または最後以外の場所での使用は、定義された実装です。通常、これは範囲に使用されますが、範囲の指定内容は文字セットによって異なります。EBCDICには文字範囲に穴があり、ASCII派生文字セットを想定している場合でも、すべての小文字がaz範囲にあると考えるのは
簡単

1
「%[^ \ n]」には、gets()と同じ問題、バッファオーバーフローがあります。最後の\ nが読み取られないという追加のキャッチがあります。これは、ほとんどのフォーマットが空白をスキップすることから始まるという事実によって隠されますが、[はそれらの1つではありません。scanfを使用して文字列を読み取るときのインスタンスがわかりません。
AProgrammer 2009

1
(以前のコメントで指摘されているように)s不要で正しくない場合があるため、入力文字列の末尾からを削除しました。[これは、形式のバリエーションではなく、独自の形式指定子sです。
paxdiablo 2015

53

この例では反転スキャンセットを使用しているため、scanfは「\ n」-改行に遭遇するまで値を取り込み続けるため、スペースも節約されます。

#include <stdio.h>

int main (int argc, char const *argv[])
{
    char name[20];
    scanf("%[^\n]s",name);
    printf("%s\n", name);
    return 0;
}

1
バッファオーバーフローに注意してください。ユーザーが50文字の「名前」を書き込んだ場合、プログラムはおそらくクラッシュします。
brunoais 2013年

3
バッファサイズがわかっているので、バッファ%20[^\n]sオーバーフローを防ぐために使用できます
osvein

スコア45で、sそこにあることの明らかなカーゴカルチャーを誰も指摘しませんでした!
Antti Haapala

22

これを使えます

char name[20];
scanf("%20[^\n]", name);

またはこれ

void getText(char *message, char *variable, int size){
    printf("\n %s: ", message);
    fgets(variable, sizeof(char) * size, stdin);
    sscanf(variable, "%[^\n]", variable);
}

char name[20];
getText("Your name", name, 20);

デモ


1
私はテストしませんでしたが、このページの他の回答に基づいて、あなたの例のscanfの正しいバッファサイズは次のようになると思います:(scanf("%19[^\n]", name);簡潔な回答の場合は+1)
Dr Beco

1
サイドノートと同じように、sizeof(char)定義では常に 1であるため、乗算する必要はありません。
paxdiablo 2016年

8

scanf()フィールド幅を指定せずに文字列を読み取るために使用しないでください。エラーの戻り値も確認する必要があります。

#include <stdio.h>

#define NAME_MAX    80
#define NAME_MAX_S "80"

int main(void)
{
    static char name[NAME_MAX + 1]; // + 1 because of null
    if(scanf("%" NAME_MAX_S "[^\n]", name) != 1)
    {
        fputs("io error or premature end of line\n", stderr);
        return 1;
    }

    printf("Hello %s. Nice to meet you.\n", name);
}

または、次を使用しますfgets()

#include <stdio.h>

#define NAME_MAX 80

int main(void)
{
    static char name[NAME_MAX + 2]; // + 2 because of newline and null
    if(!fgets(name, sizeof(name), stdin))
    {
        fputs("io error\n", stderr);
        return 1;
    }

    // don't print newline
    printf("Hello %.*s. Nice to meet you.\n", strlen(name) - 1, name);
}

6

あなたは使用することができfgets()、文字列、または使用を読み取るための機能をscanf("%[^\n]s",name);、文字列の読みが改行文字に遭遇すると終了しますので。


これがバッファオーバーフローを妨げないように注意してください
brunoais

sそこには属していません
アンティハパラ

5

getline()

現在、POSIXの一部です。

また、以前に尋ねたバッファ割り当ての問題も処理しますがfree、メモリを処理する必要があります。


標準?参考文献では、「getline()とgetdelim()はどちらもGNU拡張機能です」と引用しています。
AProgrammer 2009

1
POSIX 2008はgetl​​ineを追加します。したがって、GNUは先に進んで、バージョン2.9でglibcのヘッダーを変更しました。これにより、多くのプロジェクトで問題が発生します。決定的なリンクではありませんが、こちらをご覧ください:bugzilla.redhat.com/show_bug.cgi?id=493941。オンラインのmanページについては、googleが最初に見つけたものを取得しました。
dmckee ---元モデレーターの子猫

3

誰かがまだ探しているなら、これは私のために働いたものです-スペースを含む任意の長さの文字列を読むことです。

このシンプルでエレガントなソリューションを共有してくださったWeb上の多くのポスターに感謝します。それが機能する場合、クレジットは彼らに行きますが、エラーは私のものです。

char *name;
scanf ("%m[^\n]s",&name);
printf ("%s\n",name);

2
これはPOSIX拡張であり、ISO標準には存在しないことに注意してください。完全errnoを期すために、割り当てられたメモリもチェックしてクリーンアップする必要があります。
paxdiablo 2017年

sスキャンセットの後、そこには属していません
Antti Haapala

1

scanfちょっとしたトリックでこの目的に使用できます。実際には、ユーザーがEnter(\n)を押すまでユーザー入力を許可する必要があります。これは、スペースを含むすべての文字を考慮します。次に例を示します。

int main()
{
  char string[100], c;
  int i;
  printf("Enter the string: ");
  scanf("%s", string);
  i = strlen(string);      // length of user input till first space
  do
  {
    scanf("%c", &c);
    string[i++] = c;       // reading characters after first space (including it)
  } while (c != '\n');     // until user hits Enter
  string[i - 1] = 0;       // string terminating
return 0;
}

これはどのように機能しますか?ユーザーが標準入力から文字を入力すると、最初の空白スペースまで文字列変数に格納されます。その後、残りのエントリは入力ストリームに残り、次のscanfを待ちます。次に、for入力ストリーム(まで\n)から文字を1つずつ取得して、文字列の最後に追加するループがあります。変数の、キーボードからのユーザー入力と同じ完全な文字列を形成します。

これが誰かを助けることを願っています!


バッファオーバーフローの可能性があります。
paxdiablo 2017年

0

あなたは本当にscanf()この種のものに使用すべきではありませんが、gets()またはなどのはるかに優れた呼び出しがあるのでgetline()、それを行うことができます:

#include <stdio.h>

char* scan_line(char* buffer, int buffer_size);

char* scan_line(char* buffer, int buffer_size) {
   char* p = buffer;
   int count = 0;
   do {
       char c;
       scanf("%c", &c); // scan a single character
       // break on end of line, string terminating NUL, or end of file
       if (c == '\r' || c == '\n' || c == 0 || c == EOF) {
           *p = 0;
           break;
       }
       *p++ = c; // add the valid character into the buffer
   } while (count < buffer_size - 1);  // don't overrun the buffer
   // ensure the string is null terminated
   buffer[buffer_size - 1] = 0;
   return buffer;
}

#define MAX_SCAN_LENGTH 1024

int main()
{
   char s[MAX_SCAN_LENGTH];
   printf("Enter a string: ");
   scan_line(s, MAX_SCAN_LENGTH);
   printf("got: \"%s\"\n\n", s);
   return 0;
}

2
理由があります理由gets非推奨と削除された(stackoverflow.com/questions/30890696/why-gets-is-deprecated標準からは)。少なくとも後者にはそれを安全にする方法があるため、さらに悪いscanfことです。
paxdiablo 2017年

-1
/*reading string which contains spaces*/
#include<stdio.h>
int main()
{
   char *c,*p;
   scanf("%[^\n]s",c);
   p=c;                /*since after reading then pointer points to another 
                       location iam using a second pointer to store the base 
                       address*/ 
   printf("%s",p);
   return 0;
 }

4
これが正しい答えである理由を説明できますか?コードのみの回答は投稿しないでください。
Theo

sスキャンセットの後には所属していません
Antti Haapala
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.