이 답변에서는 텍스트 줄을 읽고 해석한다고 가정 합니다 . 아마도 사용자에게 무언가를 입력하고 RETURN을 누르라는 메시지가 표시 될 수 있습니다. 또는 일종의 데이터 파일에서 구조화 된 텍스트 행을 읽는 중일 수 있습니다.
한 줄의 텍스트를 읽고 있기 때문에 한 줄의 텍스트를 읽는 라이브러리 함수를 중심으로 코드를 구성하는 것이 좋습니다. 표준 기능은 fgets()
(를 포함하여 다른 사람이 있기는하지만, getline
). 그리고 다음 단계는 어떻게 든 해당 텍스트 줄을 해석하는 것입니다.
fgets
한 줄의 텍스트를 읽도록 호출하는 기본 레시피는 다음과 같습니다 .
char line[512];
printf("type something:\n");
fgets(line, 512, stdin);
printf("you typed: %s", line);
이것은 단순히 한 줄의 텍스트를 읽고 다시 인쇄합니다. 작성된 바와 같이 몇 가지 제한 사항이 있습니다. 번호 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
예스로 / 무응답), 당신은 말 그대로 그냥이 같은 라인의 첫 번째 문자를 잡을 수 있습니다 :
printf("type a character:\n");
fgets(line, 512, stdin);
char c = line[0];
printf("you typed %c\n", c);
(물론 사용자가 다중 문자 응답을 입력했을 가능성은 무시하고 입력 된 추가 문자는 조용히 무시합니다.)
마지막으로 사용자 가 공백을 포함 하지 않는 문자열을 입력하도록 하려는 경우 입력 행을 처리하려는 경우
hello world!
문자열 "hello"
뒤에 다른 scanf
형식 (형식 %s
이 수행 한 것)이 있기 때문에, 그 경우, 나는 조금 어리 석었습니다. 결국 그 방식으로 줄을 재 해석하는 것은 그리 쉬운 일이 아닙니다. 질문의 일부는 조금 기다려야 할 것입니다.
그러나 먼저 건너 뛴 세 가지로 돌아가고 싶습니다.
(1) 전화했습니다
fgets(line, 512, stdin);
배열로 읽어 line
들이고 512는 배열의 크기 line
이므로 fgets
오버플로하지 않도록 알고 있습니다. 그러나 512가 올바른 숫자인지 확인하려면 (특히 누군가가 크기를 변경하기 위해 프로그램을 조정했는지 확인하려면) line
선언 된 곳 을 다시 읽어야 합니다. 그것은 성가신 일이므로 크기를 동기화하는 더 좋은 두 가지 방법이 있습니다. (a) 프리 프로세서를 사용하여 크기의 이름을 지정할 수 있습니다.
#define MAXLINE 512
char line[MAXLINE];
fgets(line, MAXLINE, stdin);
또는 (b) C의 sizeof
연산자를 사용하십시오.
fgets(line, sizeof(line), stdin);
(2) 두 번째 문제는 우리가 오류를 확인하지 않았다는 것입니다. 입력을 읽을 때는 항상 오류 가능성을 확인 해야 합니다. 어떤 이유로 든 fgets
요청한 텍스트 행을 읽을 수없는 경우 널 포인터를 리턴하여이를 나타냅니다. 그래서 우리는 다음과 같은 일을해야했습니다
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
숫자 다음에 숫자가 아닌 추가 입력을 무시하기 때문에).하지만 때로는 중요합니다. 종종 우리는 그 줄 바꿈을 제거하고 싶을 것입니다. 몇 가지 방법으로 몇 분 안에 접근 할 수 있습니다. (나는 그 말을 많이 들었다는 것을 알고있다. 그러나 나는 그 모든 것들로 돌아갈 것이라고 약속한다.)
내가 당신이 말한 생각 "이 시점에서 생각을 할 수 scanf
좋은 없었다,이 다른 방법은 훨씬 더 좋을 것이다 그러나. fgets
성가신처럼 보이기 시작 소명은. scanf
이었다 너무 쉽게 나는 그것을 계속 사용 할 수 없습니다!? "
물론 scanf
원하는 경우 계속 사용할 수 있습니다 . (그리고 정말
간단한 것들, 어떤면에서는 더 간단합니다.) 그러나 제발, 17 가지 기발한 것 중 하나 때문에 당신을 실패하거나 입력으로 인해 무한 루프에 빠질 때 울지 마십시오. 예상하지 못했거나 더 복잡한 것을 수행하는 방법을 알 수없는 경우. fgets
의 실제 방해 요소를 살펴 보겠습니다 .
항상 배열 크기를 지정해야합니다. 물론 이것은 전혀 번거로운 일이 아닙니다. 버퍼 오버플로는 정말 나쁜 일이기 때문에 기능입니다.
반환 값을 확인해야합니다. 실제로, 그것은 세척입니다. scanf
올바르게 사용 하려면 반환 값도 확인해야하기 때문입니다.
등을 벗겨야합니다 \n
. 이것은 진정한 성가신 일입니다. 나는이 작은 문제가 없었 음을 지적 할 수있는 표준 기능이 있었으면 좋겠다. (아무도 기르지 마십시오 gets
.) 그러나 scanf's
17 가지의 다른 성가신에 비해 , 나는이 성가신 fgets
하루를 가져갈 것 입니다.
그럼 어떻게 합니까 당신은 줄 바꿈을 제거? 세 가지 방법 :
(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
. 그러나 이러한 기능을 올바르게 사용하는 방법을 보여주는 것은 그 자체로 하나의 이야기이며, 이미 단편화 된 내러티브로 바뀌고있는 것에서 너무 혼란 스러울 것이므로 지금은 더 이상 아무 말도하지 않겠습니다.
나머지 주요한 이야기는 입력 한 숫자 나 문자보다 더 복잡한 구문 분석을 시도 할 수 있습니다. 두 개의 숫자 나 여러 개의 공백으로 구분 된 단어 또는 특정 프레임 구두점을 포함하는 행을 읽으려면 어떻게해야합니까? 여기서는 일이 흥미로워지고를 사용하여 일을하려고 할 때 일이 복잡해질 수 scanf
있는 곳과을 사용하여 한 줄의 텍스트를 깨끗하게 읽었 으므로 훨씬 더 많은 옵션이 있습니다 fgets
. 모든 옵션에 대한 전체 이야기 아마도 책을 채울 수 있으니 여기 표면 만 긁을 수있을 것입니다.
내가 가장 좋아하는 기술은 줄을 공백으로 구분 된 "단어"로 분리 한 다음 각 "단어"로 추가 작업을 수행하는 것입니다. 이 작업을 수행하는 주요 표준 기능 중 하나는
strtok
(문제가 있으며 전체적으로 별도의 토론을 평가하는) 기능입니다. 내 자신의 선호는 각각의 깨진 "단어"에 대한 포인터 배열을 구성하기위한 전용 함수입니다 . 이 코스 노트 에서 설명하는 함수
입니다. 어쨌든 "단어"를 얻은
후에는 이미 살펴본 것과 동일한 atoi
/ atof
/ strtol
/ strtod
기능을 사용하여 각 단어를 추가로 처리 할 수 있습니다 .
역설적으로, 우리는 여기서 벗어나는 방법을 알아내는 데 상당한 시간과 노력을 들였지만 scanf
방금 읽은 텍스트 줄을 처리하는 또 다른 좋은 방법
fgets
은 그것을 전달하는 것 sscanf
입니다. 이러한 방식으로의 장점은 scanf
대부분 있지만 단점은 거의 없습니다.
입력 구문이 특히 복잡하면 "regexp"라이브러리를 사용하여 구문 분석하는 것이 적절할 수 있습니다.
마지막으로, 임의의 특수 구문 분석 솔루션을 사용할 수 있습니다. char *
포인터를 사용하여 원하는 문자를 확인 하여 한 번에 한 문자 씩 줄을 이동할 수 있습니다
. 또는 당신이 좋아하는 기능을 사용하여 특정 문자를 검색 할 수 있습니다 strchr
또는 strrchr
또는 strspn
또는 strcspn
또는 strpbrk
. 또는 이전에 건너 뛴 strtol
또는
strtod
함수를 사용하여 숫자 문자 그룹을 구문 분석 / 변환하고 건너 뛸 수 있습니다 .
말할 수있는 것이 훨씬 더 많지만,이 소개가 여러분을 시작할 수 있기를 바랍니다.
(r = sscanf("1 2 junk", "%d%d", &x, &y)) != 2
후행 숫자가 아닌 텍스트 나쁜으로 감지하지 않습니다.