표준 입력

stdin에서 사용자 입력을 일반 C. 문제는 내가 오류에 강하고 사용자를 특정 입력으로 제한하고 복잡성 측면에서 짜증나 지 않는 정상적인 구현을 원한다는 것입니다. 함수 get_strings()는 다음과 같이 입력 문자를 읽습니다. 새 줄 (\n)이없고 EOF가 없으며 모든 문자가 isalpha() 테스트입니다.하지만 공백을 유지하고 싶습니다.

검토 중에 특별한주의를 기울여야 할 몇 가지 사항 :

    – 기본적으로 사용자가 의미있는 입력없이 Enter 키를 눌렀는지 테스트하는 외부 while 루프
    -ferror ()를 사용해야합니까? fgetc는 이미 문제가 발생했을 때 EOF를 반환합니다. 사용자에게 문제가 발생했다는 것을 알리는 대신 스트림에서 읽기를 중지하십시오.
    -매번 발생하지는 않지만 발생합니다. A \ n은 stdin 스트림에 남아 있고 다음에 의미있는 입력을 얻고 싶을 때 fgetc ()는 그냥 건너 뜁니다. 여기서는 중요하지 않지만 단일 문자 예 / 아니오 질문을 할 때 문제가 발생합니다. stdin에 남아있는 모든 이전 항목을 지우는 구조로만이 문제를 제거 할 수 있습니다. 이에 대한 두 번째 코드 블록을 참조하십시오. .

그래서 제가이 문제를 완전히 잘못 처리하고 있습니까? 수용 할 수있는 더 나은 방법이 있습니까? 저에게 정말 투박해 보이고 투박한 것은 항상 나쁩니다.

/** @brief Contains the dictionary */ static char **strings = NULL; /** @brief Helps with containing the dicionary */ static char *string; /* Reads input char by char with fgetc() */ static char *get_strings() { char *string = NULL; char ch; size_t len = 0; while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { if (ch != " " && isalpha((int)ch) == 0) { fprintf(stderr, "Only [a-z] is a valid input. | \t" "| Input another or end with CTRL+D: "); continue; } string = (char*) realloc(string, len+2); if (string == NULL) { bail_out(EXIT_FAILURE, "realloc(3) failed"); } string[len++] = toupper(ch); if (len >= MAX_DATA) { bail_out(EXIT_FAILURE, "Input too long\n"); } } if (ferror(in_stream)) { bail_out(EXIT_FAILURE, "Error while reading from stream"); } } if(string) { string[len] = "\0"; } else { printf("\nFinished dictionary...\n"); } printf("Added string: %s | Input another or end with CTRL+D: ", string); return string; } /* Saves the returned strings from get_strings() in a linked list */ static void read_dict() { int index; for (index = 0; (string = get_strings()); ++index) { if (string[0] == "\0") continue; strings = (char**) realloc(strings, (index+1)*sizeof(*strings)); if (strings == NULL) { bail_out(EXIT_FAILURE, "realloc(3) failed"); } strings[index] = string; } /* Take a note of how many entries we have yet. */ dict_size = index; } 

더 간단한 경우의 두 번째 CodeBlock :

while(1) { char tmp; printf("Please enter your guess [a-z]: "); guess = fgetc(stdin); /* Jump back to start of loop */ if (guess == "\n") { continue; } /* HERE IS THE CLEAR FOR STDIN This part really just eats all remaining \ns from the user, so that later inputs can start uninterrupted. Can I get rid of it in some better way? */ while((tmp = getchar()) != "\n" && tmp != EOF); if(!isalpha(guess)) { fprintf(stderr, "Enter a valid letter [a-z]!\n"); continue; } } 

댓글

  • 빠른 참고 : < stdbool.h >의 부울을 사용하거나 1과 0이 아닌 정의를 선호합니다. ‘의 의미를 더 명확하게합니다.
  • @Zorgatone 반 정도 동의합니다. 항상 stdbool.h를 사용하지만 ‘ 자신의 부울을 굴려서는 안됩니다.

답변

아키텍처

stdin는 일반적으로 라인 버퍼링 입니다. 따라서 사용자가 Enter 를 누를 때까지 fgetc()에 아무것도 제공되지 않습니다. OP 코드는 “Hello 123″과 같은 입력과 함께 여러 오류 메시지를 제공합니다. 입력 유효성 검사에서 사용자 입력을 분리하는 것이 좋습니다. fgets() 또는 fgets()에는 몇 가지 약점이 있으므로 자신의 일부 버전으로 사용자 입력 줄을 읽으십시오. 그런 다음 입력을 확인합니다.

char *input; while ((input = my_gets()) != NULL) { if (valid_phrase(input)) { foo(input); } else { fprintf(stderr, "Invalid input\n"); } free(input); } 

“외부 while 루프를 제거하고 싶습니다.”관련. 이 루프는 "\n"를 자동으로 사용하기 위해 존재합니다. 루프를 사용하려면 내부 루프 앞에

int ch; while ((ch = fgetc()) == "\n") ; ungetc(ch, stdin); 

char ch

ch는 최상의 유형이 아닙니다. fgetc()는 일반적으로 257 개의 서로 다른 값 [0-255]EOF를 반환합니다. 제대로 구분하려면 결과를 int에 저장하세요.

// bad char ch; .. while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { // better int ch; .. while (string == NULL && ch != EOF) { while (EOF != (ch = fgetc(in_stream)) && ch != "\n") { 

realloc()

캐스트 필요 없음.
메모리 부족을 수정하여 string-코드가 단순히 종료되는 경우에는 필요하지 않지만 장난감을 넣는 것이 좋습니다 (코드 “의 포인터) 떨어져 있습니다.

// string = (char*) realloc(string, len+2); char * new_string = realloc(string, len+2); if (new_string == NULL) { free(string); bail_out(EXIT_FAILURE, "Out of memory"); } string = new_string; 

아래의 sizeof(*strings)를 잘 사용합니다. 단순화를 권장합니다.

strings = (char**) realloc(strings, (index+1)*sizeof(*strings)); strings = realloc(strings, sizeof *strings * (index+1)); 

size_t len

배열 크기를 나타내는 데 size_t를 잘 사용합니다. 흥미롭게도 코드는 int index;와 동일하지 않습니다. size_t index;

is...()

int ch를 사용하면 캐스트가 필요 없습니다. 논리 테스트이므로 산술 == 0 대신 !를 사용하는 것이 좋습니다.

// if (ch != " " && isalpha((int)ch) == 0) { if (ch != " " && !isalpha(ch)) { 

다음은 이해하기 더 쉬울 수 있습니다. 부정이 적습니다. (스타일 문제)

if (!(ch == " " || isalpha(ch))) { 

ferror()

if (ferror(in_stream))

확인 변수 이름

string, strings는 다음과 같이 유용합니다. 정수 integer를 호출합니다. 대신 phrase, dictionary 일 수 있습니다.

// OK /** @brief Contains the dictionary */ static char **strings = NULL; // Better // comment not truly needed static char **dictionary = NULL; 

get_strings()의 이름이 잘못되었습니다. 일반적으로 들리지만 코드는 문자와 공백으로 입력을 제한합니다.get_words()?

댓글

  • 같은 답변을 두 번 게시 한 것 같나요? 어쨌든 이것이 제가 찾고 있던 답입니다! 나는 fgetc sind fgets를 사용하는 데 전적으로 집중했습니다. ‘ 처음에는 작동하지 않았습니다 (stdin의 \ ns 불량 때문에). 이것은 훨씬 더 나은 것처럼 보이며 이것을 내 코드에 통합 할 것입니다. 감사합니다!
  • @Haini 당신은 수락하는 것 외에도 답변이 너무 마음에 든다면 찬성 투표를 할 수 있다는 것을 알고 있습니다;-)

답변

잘못된 재 할당 전략

현재 읽는 모든 문자에 대해 realloc()를 호출합니다. 결과적으로 문자열을 읽는 데 \ $ O (n ^ 2) \ $ 시간이 발생합니다. realloc()를 호출 할 때마다 현재 내용을 새 버퍼에 복사해야 할 수 있기 때문입니다. . MAX_DATA 크기의 버퍼를 할당 한 다음 realloc를 사용하여 마지막에 할당을 줄이거 나 재 할당 전략으로 변경해야합니다. 재 할당 크기가 매번 곱셈 요소 (예 : 2x)만큼 증가합니다.

이것은 동일한 작업을 수행하는 문자열 배열에도 적용됩니다.

이상한 들여 쓰기

내포 된 while 루프가 외부 while 루프와 동일한 들여 쓰기 수준에 있기 때문에 들여 쓰기가 이상합니다.

fgets ()를 사용 하시겠습니까?

개인적으로 fgets() (또는 )를 사용하여 문자열을 읽습니다. fgets()는 모든 직접 코딩 된 로직없이 루프가 수행하는 작업을 거의 수행합니다.

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다