programing

C 콘솔의 행을 읽는 방법

coolbiz 2022. 8. 27. 23:54
반응형

C 콘솔의 행을 읽는 방법

C 콘솔 프로그램에서 전체 줄을 읽는 가장 간단한 방법은 무엇입니까? 입력한 텍스트의 길이는 가변적일 수 있으며 텍스트의 내용에 대해서는 어떠한 가정도 할 수 없습니다.

메모리하며, " " 를 합니다.fgets을 사용법하지만 몇 글자를 읽었는지 알 수 있는 방법은 없는 것 같습니다.fgetc를 사용합니다.

char * getline(void) {
    char * line = malloc(100), * linep = line;
    size_t lenmax = 100, len = lenmax;
    int c;

    if(line == NULL)
        return NULL;

    for(;;) {
        c = fgetc(stdin);
        if(c == EOF)
            break;

        if(--len == 0) {
            len = lenmax;
            char * linen = realloc(linep, lenmax *= 2);

            if(linen == NULL) {
                free(linep);
                return NULL;
            }
            line = linen + (line - linep);
            linep = linen;
        }

        if((*line++ = c) == '\n')
            break;
    }
    *line = '\0';
    return linep;
}

참고: 절대 gets를 사용하지 마십시오! 경계 검사를 수행하지 않으며 버퍼가 오버플로할 수 있습니다.

명령어 인수를 찾고 있다면 Tim의 답변을 보세요.콘솔에서 한 줄만 읽는 경우:

#include <stdio.h>

int main()
{
  char string [256];
  printf ("Insert your full address: ");
  gets (string);
  printf ("Your address is: %s\n",string);
  return 0;
}

네, 안전하지 않고 버퍼 오버런을 실행할 수 있으며 파일 끝을 확인하지 않으며 인코딩 및 기타 많은 기능을 지원하지 않습니다.사실 나는 그것이 이런 것들을 할 수 있는지조차 생각하지 않았다.내가 좀 망쳤다는 건 인정해:) 하지만..."C의 콘솔에서 한 줄을 읽는 방법?"과 같은 질문을 받으면 위와 같은 100줄의 코드가 아니라 gets()와 같은 간단한 것이 필요할 것입니다.실제로 100줄의 코드를 작성하려고 하면 gets를 선택했을 때보다 훨씬 더 많은 실수를 하게 될 것이라고 생각합니다.

정적 할당을 위해 행을 읽는 매우 단순하지만 안전하지 않은 구현:

char line[1024];

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

버퍼 오버플로우 가능성이 없지만 행 전체를 읽지 않을 가능성이 있는 보다 안전한 실장은 다음과 같습니다.

char line[1024];

scanf("%1023[^\n]", line);

변수를 선언하는 지정된 길이와 형식 문자열에서 지정된 길이의 '1에 의한 차이'가 아닙니다.그것은 역사적인 유물입니다.

GNU C 라이브러리 또는 다른 POSIX 호환 라이브러리를 사용하는 경우 를 사용하여 전달할 수 있습니다.stdin파일 스트림에 대해 설명합니다.

getline(실행 가능 예)

getline답변에는 언급되어 있습니다만, 여기 예가 있습니다.

POSIX 7 이며 메모리를 할당하고 할당된 버퍼를 루프 상에서 적절하게 재사용합니다.

포인터 뉴브스, 다음을 읽습니다.getline의 첫 번째 인수가 "char*"가 아닌 "char**" 포인터에 대한 포인터인 이유는 무엇입니까?

메인

#define _XOPEN_SOURCE 700
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    char *line = NULL;
    size_t len = 0;
    ssize_t read = 0;
    while (1) {
        puts("enter a line");
        read = getline(&line, &len, stdin);
        if (read == -1)
            break;
        printf("line = %s", line);
        printf("line length = %zu\n", read);
        puts("");
    }
    free(line);
    return 0;
}

컴파일 및 실행:

gcc -ggdb3 -O0 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
./main.out

결과: 온도:

enter a line

다음에, 다음과 같이 입력합니다.

asdf

Enter 키를 누르면 다음과 같이 표시됩니다.

line = asdf
line length = 5

그 다음에 또 다른 것이 있습니다.

enter a line

또는 파이프에서 stdin까지:

printf 'asdf\nqwer\n' | ./main.out

다음과 같은 기능이 있습니다.

enter a line
line = asdf
line length = 5

enter a line
line = qwer
line length = 5

enter a line

Ubuntu 20.04로 테스트.

glibc 실장

POSIX는 없나요?glibc 2.23의 실장을 검토해 주십시오.

getdelim은 POSIX 슈퍼셋의 한 POSIX getline이치노

증설이 필요할 때마다 할당된 메모리가 2배로 증가하여 스레드 세이프해 보입니다.

약간의 매크로 확장이 필요하지만, 더 나은 결과를 얻을 수 있을 것 같지는 않습니다.

C 콘솔의 행을 읽는 방법

  • 자체 기능을 구축하는 것은 콘솔에서 행을 읽는 데 도움이 되는 방법 중 하나입니다.

  • 동적 메모리 할당을 사용하여 필요한 메모리 양을 할당하고 있습니다.

  • 할당된 메모리를 소진하려고 할 때 메모리 크기를 두 배로 늘리려고 합니다.

  • 스캔을 하고 .getchar()가 입력될 합니다.'\n' ★★★★★★★★★★★★★★★★★」EOF 표시

  • 마지막으로 회선을 반환하기 전에 추가 할당된 메모리를 제거합니다.

//the function to read lines of variable length

char* scan_line(char *line)
{
    int ch;             // as getchar() returns `int`
    long capacity = 0;  // capacity of the buffer
    long length = 0;    // maintains the length of the string
    char *temp = NULL;  // use additional pointer to perform allocations in order to avoid memory leaks

    while ( ((ch = getchar()) != '\n') && (ch != EOF) )
    {
        if((length + 1) >= capacity)
        {
            // resetting capacity
            if (capacity == 0)
                capacity = 2; // some initial fixed length 
            else
                capacity *= 2; // double the size

            // try reallocating the memory
            if( (temp = realloc(line, capacity * sizeof(char))) == NULL ) //allocating memory
            {
                printf("ERROR: unsuccessful allocation");
                // return line; or you can exit
                exit(1);
            }

            line = temp;
        }

        line[length] = (char) ch; //type casting `int` to `char`
        length++;
    }
    line[length + 1] = '\0'; //inserting null character at the end

    // remove additionally allocated memory
    if( (temp = realloc(line, (length + 1) * sizeof(char))) == NULL )
    {
        printf("ERROR: unsuccessful allocation");
        // return line; or you can exit
        exit(1);
    }

    line = temp;
    return line;
}
  • 이렇게 하면 전체 행을 읽을 수 있습니다.

     char *line = NULL;
     line = scan_line(line);
    

다음 예제에서는 다음을 사용하는 프로그램 예를 보여 줍니다.scan_line() 삭제:

#include <stdio.h>
#include <stdlib.h> //for dynamic allocation functions

char* scan_line(char *line)
{
    ..........
}

int main(void)
{
    char *a = NULL;

    a = scan_line(a); //function call to scan the line

    printf("%s\n",a); //printing the scanned line

    free(a); //don't forget to free the malloc'd pointer
}

입력 예:

Twinkle Twinkle little star.... in the sky!

출력 예:

Twinkle Twinkle little star.... in the sky!

제시된 바와 같이 getchar()를 사용하여 엔드 오브 라인 또는 EOF가 반환될 때까지 콘솔에서 읽을 수 있으며 자체 버퍼를 구축할 수 있습니다.적절한 최대 회선 사이즈를 설정할 수 없는 경우 버퍼가 동적으로 증가할 수 있습니다.

fgets 는, C 의 늘 종단 문자열로서 회선을 취득하는 안전한 방법으로서도 사용할 수 있습니다.

#include <stdio.h>

char line[1024];  /* Generously large value for most situations */

char *eof;

line[0] = '\0'; /* Ensure empty line if no input delivered */
line[sizeof(line)-1] = ~'\0';  /* Ensure no false-null at end of buffer */

eof = fgets(line, sizeof(line), stdin);

콘솔 입력을 모두 사용했거나 어떤 이유로 작업이 실패하면 eof == NULL이 반환되고 줄 버퍼가 변경되지 않을 수 있습니다(이 때문에 첫 문자를 '\0'으로 설정하면 편리합니다).

fgets는 행[]을 과도하게 채우지 않고 정상적으로 반환되었을 때 마지막 문자 뒤에 늘이 있음을 보증합니다.

행의 끝에 도달했을 경우, 종료하는 「\0」앞의 문자는 「\n」이 됩니다.

"\0" 종료 전에 "\n" 종료가 없는 경우, 더 많은 데이터가 있거나 다음 요청에서 파일 종료를 보고할 수 있습니다.어떤 fget을 사용하여 어떤 fget을 사용할지 결정해야 합니다(이 점에서 getchar()를 사용하여 루프하는 것이 더 쉽습니다).

위의 예제 코드에서는 fget이 성공한 후 line[size of(line)-1] == '\0'이면 버퍼가 완전히 채워진 것을 알 수 있습니다.그 위치가 '\n'로 진행된다면 당신은 운이 좋았다는 것을 알 수 있습니다.그렇지 않으면 stdin에서 더 많은 데이터가 있거나 파일 끝 부분이 앞에 있습니다.(버퍼가 완전히 채워지지 않은 경우에도 파일 끝에 있을 수 있으며 현재 행 끝에 "\n"이 없을 수도 있습니다.문자열의 끝(버퍼의 첫 번째 "\0") 전에 문자열을 스캔하여 "\n"을 찾거나 삭제해야 하기 때문에 우선 getchar()를 사용하는 것이 좋습니다.

첫 번째 청크로 읽은 양보다 더 많은 행이 아직 남아 있는 경우에 대처하기 위해 필요한 작업을 수행합니다.버퍼를 동적으로 확장하는 예는 getchar 또는 fget 중 하나로 동작시킬 수 있습니다.주의해야 할 몇 가지 까다로운 엣지 케이스가 있습니다(예를 들어 버퍼가 확장되기 전에 이전 입력을 종료한 '\0' 위치에서 다음 입력이 저장되도록 기억하십시오).

나도 얼마 전에 같은 문제에 부딪혔는데, 이건 내 위자료였어. 도움이 됐으면 좋겠어.

/*
 * Initial size of the read buffer
 */
#define DEFAULT_BUFFER 1024

/*
 * Standard boolean type definition
 */
typedef enum{ false = 0, true = 1 }bool;

/*
 * Flags errors in pointer returning functions
 */
bool has_err = false;

/*
 * Reads the next line of text from file and returns it.
 * The line must be free()d afterwards.
 *
 * This function will segfault on binary data.
 */
char *readLine(FILE *file){
    char *buffer   = NULL;
    char *tmp_buf  = NULL;
    bool line_read = false;
    int  iteration = 0;
    int  offset    = 0;

    if(file == NULL){
        fprintf(stderr, "readLine: NULL file pointer passed!\n");
        has_err = true;

        return NULL;
    }

    while(!line_read){
        if((tmp_buf = malloc(DEFAULT_BUFFER)) == NULL){
            fprintf(stderr, "readLine: Unable to allocate temporary buffer!\n");
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        if(fgets(tmp_buf, DEFAULT_BUFFER, file) == NULL){
            free(tmp_buf);

            break;
        }

        if(tmp_buf[strlen(tmp_buf) - 1] == '\n') /* we have an end of line */
            line_read = true;

        offset = DEFAULT_BUFFER * (iteration + 1);

        if((buffer = realloc(buffer, offset)) == NULL){
            fprintf(stderr, "readLine: Unable to reallocate buffer!\n");
            free(tmp_buf);
            has_err = true;

            return NULL;
        }

        offset = DEFAULT_BUFFER * iteration - iteration;

        if(memcpy(buffer + offset, tmp_buf, DEFAULT_BUFFER) == NULL){
            fprintf(stderr, "readLine: Cannot copy to buffer\n");
            free(tmp_buf);
            if(buffer != NULL)
                free(buffer);
            has_err = true;

            return NULL;
        }

        free(tmp_buf);
        iteration++;
    }

    return buffer;
}

버퍼 오버플로가 발생하지 않도록 하고 입력을 잘라내지 않도록 하기 위해 문자 단위(getc) 루프를 사용해야 할 수 있습니다.

BSD 시스템 및 Android에서는fgetln:

#include <stdio.h>

char *
fgetln(FILE *stream, size_t *len);

다음과 같은 경우:

size_t line_len;
const char *line = fgetln(stdin, &line_len);

linenull로 종료되지 않고 다음을 포함합니다.\n(또는 사용 중인 플랫폼이 무엇이든) 최종적으로는,스트림에서 다음 I/O 작업을 수행한 후에는 유효하지 않게 됩니다.

다음과 같은 경우:

unsigned int getConsoleInput(char **pStrBfr) //pass in pointer to char pointer, returns size of buffer
{
    char * strbfr;
    int c;
    unsigned int i;
    i = 0;
    strbfr = (char*)malloc(sizeof(char));
    if(strbfr==NULL) goto error;
    while( (c = getchar()) != '\n' && c != EOF )
    {
        strbfr[i] = (char)c;
        i++;
        strbfr = (void*)realloc((void*)strbfr,sizeof(char)*(i+1));
        //on realloc error, NULL is returned but original buffer is unchanged
        //NOTE: the buffer WILL NOT be NULL terminated since last
        //chracter came from console
        if(strbfr==NULL) goto error;
    }
    strbfr[i] = '\0';
    *pStrBfr = strbfr; //successfully returns pointer to NULL terminated buffer
    return i + 1; 
    error:
    *pStrBfr = strbfr;
    return i + 1;
}

콘솔에서 행을 읽는 가장 간단한 방법은 getchar() 함수를 사용하는 것입니다.getchar() 함수는 한 번에 한 문자를 배열에 저장합니다.

{
char message[N];        /* character array for the message, you can always change the character length */
int i = 0;          /* loop counter */

printf( "Enter a message: " );
message[i] = getchar();    /* get the first character */
while( message[i] != '\n' ){
    message[++i] = getchar(); /* gets the next character */
}

printf( "Entered message is:" );
for( i = 0; i < N; i++ )
    printf( "%c", message[i] );

return ( 0 );

}

이를 위한 최소한의 구현은 다음과 같습니다.좋은 점은 '\n'은 유지되지 않지만 보안을 위해 읽을 수 있는 크기를 지정해야 한다는 것입니다.

#include <stdio.h>
#include <errno.h>

int sc_gets(char *buf, int n)
{
    int count = 0;
    char c;

    if (__glibc_unlikely(n <= 0))
        return -1;

    while (--n && (c = fgetc(stdin)) != '\n')
        buf[count++] = c;
    buf[count] = '\0';

    return (count != 0 || errno != EAGAIN) ? count : -1;
}

테스트 대상:

#define BUFF_SIZE 10

int main (void) {
    char buff[BUFF_SIZE];

    sc_gets(buff, sizeof(buff));
    printf ("%s\n", buff);

    return 0;
}

NB: 회선 반환을 찾는 데 INT_MAX로 제한됩니다.이 정도면 충분합니다.

언급URL : https://stackoverflow.com/questions/314401/how-to-read-a-line-from-the-console-in-c

반응형