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 Aardvark, scanf()이후 모든 전원 단지 상처 Lucas. scanf()공백 을 허용 하는 방법


9
좀 더 관용적 인 표현은 'malloc (sizeof (char) * 256 + 1)'또는 'malloc (256 + 1)'이거나 더 좋습니다 ( 'name'이 로컬에서 엄격하게 사용된다고 가정) 'char name [256 + 1 ] '. '+1'은 널 터미네이터의 니모닉으로 작동 할 수 있으며 할당에 포함되어야합니다.
Barry Kelly

@Barry- 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
Kredns

7
사용자로부터 라인을 얻으려면 더 쉽습니다. 버퍼 오버플로를 피할 수 있으므로 더 안전합니다. scanf 패밀리는 문자열을 다른 문자로 바꾸는 데 실제로 유용합니다 (예 : "% c % c % c % c % d"와 같은 4 개의 문자 및 정수). 그러나 그럼에도 불구하고 fgets 및 sscanf를 사용해야합니다. 버퍼 오버플로 가능성을 피하기 위해 scanf.
paxdiablo

4
최대 버퍼 크기를 scanf 형식으로 넣을 수 있습니다. 런타임에서 형식을 작성하지 않고 런타임 계산 된 버퍼를 넣을 수는 없습니다 (printf의 *와 같지 않으며 *는 다른 동작으로 scanf의 유효한 수정 자입니다. ).
AProgrammer

참고 또한 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) 여기의 마지막은 형식 지시문이 아니지만 scanf는 정확하게 일치시키기 위해 여기에서 시도합니다. 이 효과는 1234567890과 같은 항목에서 볼 수 있지만 s는 소비되지만 어디에도 두지 않습니다. 다른 편지는 소비되지 않습니다. s 뒤에 다른 형식을 넣으면 s가 일치하는 경우에만 형식이 읽 힙니다.
AProgrammer

또 다른 잠재적 인 문제는 첫 번째 또는 마지막 이외의 다른 장소에서 사용되는 구현입니다. 일반적으로 범위에 사용되지만 범위가 지정하는 것은 문자 집합에 따라 다릅니다. EBCDIC는 문자 범위에 구멍이 있으며 ASCII 파생 문자 세트를 가정 할 때 모든 소문자가 az 범위에 있다고 생각하는 것은 순진합니다.
AProgrammer

1
"% [^ \ n]"은 gets (), 버퍼 오버 플로우와 같은 문제가 있습니다. \ n final이 읽히지 않는다는 추가 캐치로; 이것은 대부분의 형식이 공백을 건너 뛰는 것으로 시작한다는 사실에 숨겨져 있지만 [는 그중 하나가 아닙니다. scanf를 사용하여 문자열을 읽는 인스턴스를 이해하지 못합니다.
AProgrammer

1
s입력 문자열의 끝 부분을 제거했습니다 (이전 주석에서 지적한 바와 같이) 경우에 따라 불필요하고 잘못 되었습니다. [그것의이다 자신의 형식 지정자보다는 몇 가지 변화 s중 하나.
paxdiablo

54

이 예에서는 반전 스캔 셋을 사용하므로 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

3
버퍼 크기를 알고 있듯이 %20[^\n]s버퍼 오버플로를 방지 하는 데 사용할 수 있습니다.
osvein

45 점을 받았는데 아무도 s거기에있는 화물을 명백하게 가르쳤다 !
Antti Haapala 2016 년

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)
박사 BECO

1
부수적으로, sizeof(char)정의에 따르면 항상 1이므로 곱할 필요가 없습니다.
paxdiablo

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거기에 속하지 않는
Antti Haapala

5

getline()

이제 POSIX의 일부입니다.

또한 free메모리를 관리해야하지만 이전에 요청한 버퍼 할당 문제도 처리 합니다.


표준? 참고로 "getline ()과 getdelim ()은 모두 GNU 확장입니다."
AProgrammer

1
POSIX 2008은 getline을 추가합니다. 따라서 GNU는 2.9 버전에서 glibc의 헤더를 변경했으며 많은 프로젝트에서 문제를 일으키고 있습니다. 결정적인 링크는 아니지만 bugzilla.redhat.com/show_bug.cgi?id=493941을 참조하십시오 . 온라인 맨 페이지는 Google이 찾은 첫 번째 페이지를 가져 왔습니다.
dmckee ---

3

누군가가 여전히 찾고 있다면, 여기 나를 위해 일한 것이 있습니다-공백을 포함하여 임의의 길이의 문자열을 읽으십시오.

이 단순하고 우아한 솔루션을 공유 한 웹상의 많은 포스터 덕분입니다. 그것이 작동한다면 크레딧은 그들에게 간다. 그러나 어떤 오류도 내 것이 아니다.

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

2
이것이 POSIX 확장이며 ISO 표준에는 존재하지 않습니다. 완전성 errno을 위해 할당 된 메모리 도 확인 하고 정리해야합니다.
paxdiablo

s스캔 세트 후 거기에 속하지 않는
Antti Haapala

1

scanf약간의 트릭으로이 목적으로 사용할 수 있습니다 . 실제로 사용자가 Enter ( \n)를 누를 때까지 사용자 입력을 허용해야합니다 . 이것은 space를 포함한 모든 캐릭터를 고려할 것 입니다. 예를 들면 다음과 같습니다.

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;
}

어떻게 작동합니까? 사용자가 표준 입력에서 문자를 입력하면 첫 번째 공백까지 문자열 변수에 저장됩니다 . 그 후, 나머지 입력은 입력 스트림에 남아 있고 다음 스캔을 기다립니다. 다음 for으로 입력 스트림에서 char마다 char을 가져 와서 문자열 변수의 \n끝 부분에 할당하여 키보드의 사용자 입력과 동일한 완전한 문자열을 형성 하는 루프가 있습니다.

이것이 누군가를 도울 수 있기를 바랍니다!


버퍼 오버 플로우가 발생합니다.
paxdiablo

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 1

-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.