[C언어] #5 입력 (콘솔 입력, 파일입출력, 커맨드라인)

입력의 출처는 어디?

  • 스트림(콘솔, 파일) 문자열 등

입력처리 전략

1. 한 글자씩 읽기

  • 입력이 문자/문자열일 때 좋음
    • 다른 데이터형은 좀 쓰기 어려울수도 (숫자 1004를 문자 하나씩..?)
  • 쓸데없이 메모리에 입력값을 저장하지 않아도 된다.
  • for문 한번으로 처리가능 O(N)
  • 사용예시
int c;
c = getchar();

while(c != EOF){
    putchar(c);
    c = getchar();
}
  • 입력을 하면 buffer에 문자열이 들어간다.
  • 엔터(개행문자)를 치면 입력이 종료되고 버퍼에 값이 있으면 하나씩 읽어서 출력한다. (읽은 값이 EOF이면 종료)
    • EOF는 특별한 단축키로 사용 (윈도우 ctrl + z, 유닉스(리눅스) ctrl + d)
  • 버퍼에 값이 없으면 다시 입력을 대기한다.

getchar()

int getchar(void)
  • 반환값이 int
    • EOF : 입력의 끝을 나타내는 값
    • C 표준에 따르면 EOF는 음수인데 char형은 부호가 있을 수도 있고 없을 수도 있기 때문에 int형을 반환해야만 한다.
  • 키보드로부터 문자를 하나읽음
    • 성공시 읽은 문자의(아스키코드) 반환
    • 실패시, EOF를 반환

2. 한 줄씩 읽기

하나씩 읽는 것 보다 한 줄씩 읽는 게 빠름

  • CPU를 벗어나 외부 구성요소에서 뭔가를 읽어올 때는 한번에 많이 읽어오는게 빠름.
  • 따라서 버퍼 크기가 충분히 큰 것이 좋다.

한 데이터씩 읽어오는 것과 같이 사용하면 유용하다. (안전하게 사용가능)

한 줄을 읽어오면 저장할 공간을 함수가 할당해주지 않는다.

  • 함수를 사용만 하고 메모리 해제하지 않으면 메모리 누수 문제가 발생할 수 있기 때문에
  • 미리 만든 배열을 함수에 전달해야 한다.

gets()

char* gets(char* str);
  • 콘솔에서 \n또는 EOF를 만날 때까지 계속 문자들을 읽어서 str 배열에 저장 (개행문자는 저장안함 대신 \0저장)

  • 마지막 문자 뒤에 \0문자 넣어줌

  • 성공시, str

  • 실패시 NULL 포인터

  • 매우 위험한 함수, 그냥 사용안하면 됨 (C11 부터 사라짐)

    • 위험한 이유는 버퍼 사이즈 이상의 입력이 들어오면 버퍼오버플로우 발생, 이를 통제할 방법이 없다.

    • 버퍼오버플로우

      • stdin --> str (stdin사이즈가 더 크면 str 뒤쪽메모리까지...)

      • 버퍼로 잡아놓은 사이즈를 초과해서 다른 메모리를 키보드 입력으로 덮어씌워버린다.

      • 함수 내부에 다른 용도로 잡아놓은 메모리주소(매개변수, 돌아갈 함수 주소, 베이스 포인터)에 값이 변경되어 버릴수 있다!

      • 이를 악용한 보안 공격도 가능(버퍼오버플로우 어택)

fgets()

char* fgets(char* str, int count, FILE* stream);
  • 최대 count-1개의 문자열만 읽어서 str에 저장
    • str의 길이보다 count가 더 크다면 gets()함수와 동일한 문제 발생할 수 있다.
  • str에 개행문자까지 저장해준다.
  • 키보드 입력을 읽어오고 싶다면 stdin을 스트림에 넣어주면됨.
#define LINE_LENGTH(10);

char line[LENGTH];

while(fgets(line, LINE_LENGTH, stdin) != NULL){
    printf("%s", line);
}
  • 엔터 입력시 까지 계속 입력받는다.
  • 버퍼에 값이 있다면 line에 지정한 숫자만큼만 배열에 넣어주고 종료문자열 삽입.
  • 버퍼에 값이 없다면 종료

3. 한 데이터씩 읽기

콘솔 입력으로부터 읽음

int scanf(const char* format ...)

파일 스트림으로부터 읽음

int fscanf(FILE* stream, const char* format ...)

C스타일 문자열로부터 읽음

int sscanf(const char* buffer, const char* format ...)

scanf()

  • 반환하는 값은 읽은 데이터의 개수를 반환
  • 원본을 직접 변경하기 때문에 주소값을 전달
    • 복사된 매개변수를 전달해서 값을 변경해봤자.. 함수 종료시 사라진다.
  • 데이터의 구분을 공백문자로 하기때문에 모든 공백문자는 사라진다.
    • %c로 한 문자씩 읽을 때만 공백문자 저장가능
  • 배열크기 보다 큰 데이터가 입력으로 들어오면.. 버퍼 오버플로
  • 입력에 실패하면 그 위치에 포인터를 가지고 있기 때문에 무한루프에 빠질 가능성이 꽤 높다.

fgets()와 sscanf()를 같이 사용하면 좋다.

#define LINE_LEGNTH(1024);

int sum = 0;
int num;
char line[LINE_LENGTH];

while(TRUE){
    if(fgets(line, LINE_LENGTH, stdin) == NULL){
        clearerr(stdin);
        break;
    }
    
    if (sscanf(line, "%d", &num) == 1){
        sum += num;
    }
}

4. 한 블록씩 읽기(이진 데이터)

이진 데이터를 읽는 방법

  • 파일 형태
size_t fread(void* buffer, size_t size, size_t count, FILE* stream);

size 바이트짜리 데이터를 총 count개수만큼 읽어서 buffer에 저장

EOF를 만나면 멈춤

int nums[64];
size_t num_read;
FILE* fstream; // 파일 스트림

num_read = fread(nums, sizeof(nums[0]), 64, fstream);
fwrite(nums, sizeof(nums[0]), 64, fstream);

파일입출력

C언어에서 파일다루기

  1. 파일을 열어서 파일 스트림을 가져옴
  2. 파일 스트림을 사용해서 하고 싶은 걸 한다
  3. 그 파일을 닫아줌

파일열기

FILE* fopen(const char* filename, const char* mode);

파일을 열고 꼭 닫아줘야한다.

  • 파일은 운영체제가 열어주는 것.
  • 운영체제는 우리가 언제 파일을 다써서 필요없는지 알 수 없다.
  • 계속 파일을 열기만 하면 어느순간 더 이상 파일을 열 수 없다며 뻗을 수 있다.
  • 프로그래머의 습관이 가장 중요하다(여는 코드를 작성하는 동시에 닫는 코드도 같이 작성)

파일열기에 실패하면 리턴값은 NULL포인터

파일닫기

int fclose(FILE* stream);

입출력 리디렉션

모든 프로그램은 실행될 때 3개의 스트림이 생성된다. (stdin stdout stderr)

키보드 입력을 파일에서, 콘솔 출력을 파일로 (소스변경없이, fopen이런거 없이!)

C언어의 기능이 아니라 커맨드라인, shell의 기능

파일을 다 읽으면 EOF가 자동으로 추가

리디렉션 != fopen()

> a.exe < input.txt > output.txt 2> error.txt

stdin : < 사용( input.txt에 있는 데이터를 키보드입력처럼 사용하기 )

stdout : > 사용 (콘솔출력을 output.txt에 기록하기)

stderr : 2> 사용 (콘솔출력을 error.txt에 기록하기)

좋은 웹페이지 즐겨찾기