Qaupot Blog
Software Engineering, Trip

406. 입출력 함수와 서식문자

🕐 Fri, 07 Feb 2014 09:00:00 GMT 🕓 Tue, 24 Aug 2021 02:42:00 GMT

프로그래밍 언어와 입출력

프로그램에 입력 기능이 없다면, 프로그램은 처음 컴파일될 당시의 모습으로만 동작해야 합니다. 프로그램에 출력 기능이 없다면, 프로그램이 만들어낸 결과를 확인하기 어렵습니다.

입출력은 OS 혹은 프로그래밍 언어에서 굉장히 중요합니다.

유저와의 대화를 컴퓨터는 입출력 기능을 통하기 때문입니다. C/C++에서는 언어 단위에서 입출력을 지원하는것은 아니지만, 표준 함수 라이브러리를 통해 이를 지원하고 있습니다.

이번 장에서는 간단히 몇 가지 입출력 함수를 다룹니다.

표준 입출력

표준 입출력은, 유저나 OS의 요청에 따라 그 입출력 위치가 달라질 수 있다는것을 의미합니다.

대부분의 경우 화면상에서 출력, 입력을 받게 되지만, 때에 따라서는 다른 방법으로 진행 될 수도 있습니다.
출력의 경우 파일 혹은 프린팅으로 처리 되기도 합니다. 여기서는 printf 와 scanf 를 다룹니다.

printf 와 scanf 의 끝에 f의 의미는 "format"이라는 의미입니다.

  • 형식에 맞추어 입력 혹은 출력을 하는 함수라는 뜻으로 볼 수 있습니다.
  • 이들을 배우기 앞서 형식, 즉 서식 문자라는것을 배워야 합니다.
서식문자 형태
%d 부호가 있는 10진수 정수
%u 부호가 없는 10진수 정수
%o 부호가 없는 8진수 정수
%x 부호가 없는 16진수 정수
%f 10진수 실수
%e e 표기법의 10진수 실수
%c 문자
%s 문자열

이 외에도 몇 가지 더 존재하지만, 비교적 자주 사용되는 것만 언급하였습니다.

표준 출력 - printf

#include <stdio.h>

int main(int argc,char** argv)
{
    printf("%d %u %o %x %c %s %f %e",100,100,100,100,'c',"abcde",0.1,0.1);
    return 0;
}

printf 함수는 첫 번째 인수로 서식문자가 포함된 문자열을 받습니다.

  • 서식문자는 두 번째 인수부터 들어온 값들을 의미하게 되며, 서식문자에 지정된 서식으로 해당 값을 치환합니다.

이러한 서식문자에는 자릿수를 설정할수도 있습니다.

  • %d의 경우 %5d라고 하면 5자리수를 표현하되 우측 정렬
  • %-5d 라고 쓰면 5자리수를 표현하되 좌측정렬
  • %05d 라고 쓰면 나머지 빈 자리를 0으로 채움

%f의 경우 %5.1f같은 형태를 사용할 수 있으며, %d 형태에서 소숫점 정렬이 포함된 부분입니다.

  • 위의 경우는 소숫점 한 자리까지 표현됩니다.

%s의 경우, 문자열을 전부 다 읽어들이는것이 아닌 단어 혹은 마디로 끊어 읽어들입니다.

  • 구분은 개행문자(엔터)가 될 수도 있고, 공백문자(스페이스)가 될 수도 있습니다.

한줄을 한번에 읽고 싶다면 다른 함수 (gets) 를 이용해야 합니다.

  • \n (OS 혹은 키보드에 따라 역슬러시 혹은 원화로 표기됩니다) 은 개행문자를 적는 방법입니다.

표준 입력 - scanf

#include <stdio.h>

int main(int argc,char** argv)
{
    int IntValue;
    printf("Input Your IntValue : "); 
    scanf("%d",&IntValue);
    printf("Your IntValue Is : %d",IntValue);
    return 0;
}

scanf 함수는 입력을 받는 함수입니다.

scanf 함수가 호출된 지점에서 입력을 대기하며, 입력이 들어오면 서식문자에 맞추어 그 입력을 받고 프로그램이 재개됩니다.

주의할 사항은 이러한 입력은 버퍼를 거쳐서 들어옵니다.

  • CPU 와 입력하는 키보드의 속도는 차이가 나므로, 버퍼를 사용합니다.
  • 흔히 255자 정도로 그 크기가 정해져 있으며 이를 넘어설 수 없습니다.

scanf 는 정확히 서식문자에 대해서만 데이터를 받으므로 그 이후의 데이터는 그대로 버퍼에 남게 되며,
다음 번 scanf 호출 때 그 값이 들어올 수도 있습니다.

이를 비우기 위해서는 fflush 함수를 별도로 사용합니다.

  • fflush(stdin)

두 번째 인수부터 데이터를 받을 변수를 설정하며, 인수들은 데이터를 받을 변수의 주소 값입니다.

인수는 결국 해당 함수의 지역변수이므로 함수 내에서 어떤 처리를 하더라도 사라집니다. 따라서 주소를 인수로 받으므로써, 해당 주소를 따라가 실질적인 변수 값을 변경할 수 있습니다. 주로 참조호출이라고 부릅니다.

서식 문자에 %*d %*c같은 표현을 사용할 수 있는데, 이 경우 이 서식문자에 해당하는 문자를 입력받지 않고 버립니다.

파일 입출력

표준 입출력과는 다르게 이러한 함수들은 그 범위가 파일에 대해서 한정됩니다.

  • linux같은 OS는 커널이 관리하는 가상의 파일들이 존재하며, 지시사항등을 보내는데도 이용됩니다.

파일 입출력을 나누면 크게 고 수준과 저 수준으로 나눌 수 있습니다.

본래 하드디스크등의 저장장치에는 특수한 파티션과 포맷 규격으로 데이터가 저장됩니다.
우리가 보기에 하나의 파일이라고 할 지라도 내부적으로는 이곳 저곳에 분산되어 쪼개져있을 가능성이 높습니다.
고 수준의 입출력은 이런것과는 관계없이 운영체제에서 하나의 파일로 읽어들일 수 있도록 지원해줍니다.

반면, 저 수준의 입출력을 사용할경우 메모리를 접근하는 것 처럼 디스크 자체에 접근하게 되며
약속된 포맷 규격에 맞추어 읽어들여야 하게 됩니다.

여기서는 고수준의 파일 입출력을 다룹니다. 고수준 파일 입출력에는 특수한 포인터가 사용됩니다.

이 포인터는 특수한 자료형을 가르키며, 이 자료형은 파일에 접근하기 위한 정보를 포함합니다.

  • 위와 같은 자료형은 구조체 라는 방법으로 선언 됩니다.
#include <stdio.h>

int main(int argc,char** argv)
{
    FILE* InputPointer;
    FILE* OutputPointer;
    char Strings[255];

    InputPointer = fopen("input.txt","r");
    OutputPointer = fopen("output.txt","w");

    fscanf(InputPointer,"%s",Strings);
    fprintf(OutputPointer,"Strings : %s",Strings);

    fclose(InputPointer);
    fclose(OutputPointer);
    return 0;
}

input 파일과 output 파일 둘을 준비할 것이므로, 두개의 포인터를 만들었습니다.
그리고 fopen 함수를 통해 원하는 파일을 열었습니다.

fopen은 두개의 문자열 인수를 받습니다.

  • 첫 인수는 파일이름(현재 실행파일이 동작하는 위치로부터의 상대 경로)
  • 두번째 인수는 접근 권한

접근권한은 w,r,a 등이 존재합니다.

  • w는 write(파일이 존재할 경우 겹쳐쓰기)
  • r은 read
  • a는 append(덧 붙이기) 입니다.

각각 output 파일은 쓰기 권한, input 파일은 읽기 권한으로 파일을 열었습니다.

이 과정에서 실패 했을 경우, 이 함수들은 NULL(0)을 반환합니다.
실패하는 경우는, 이미 이 파일을 다른 프로그램이 사용하고 있거나, 파일 이름이 잘 못 되었을 경우 등입니다.

  • 이 코드에서는 별다른 에러처리는 하지 않았습니다.

fscanf 와 fprintf 는 표준 입출력과 사용 방식이 비슷하지만, 첫번째 인수에 파일 포인터를 넣어 파일을 명시해주어야 합니다.

fclose 는 열었던 파일 포인터를 닫습니다.

  • 프로그램이 종료되면 OS가 알아서 파일포인터들을 전부 닫아주지만, 다른 프로그램들과의 협력을 위해서 닫아서 반환해주는것이 좋습니다.
  • 파일 역시 표준입출력처럼 버퍼라는 과정을 거치므로, 파일 포인터를 닫거나 버퍼를 비워주기 전까지는 실제 파일이 반영되지 않습니다.
  • 프로그램이 동작하는동안 바뀌어서는 안 되는 파일은 계속해서 열어두면 다른 프로그램은 이 파일을 사용하지 못 하게 됩니다.

프로그램을 직접 실행시켰을 경우 프로그램을 실행한 당시의 경로를 따릅니다. 대부분의 경우에는 프로그램 실행파일이 위치하는 경로가 되겠지만,
Visual Studio 디버거 등을 사용해서 실행 중일 경우, 프로그램의 경로는 프로젝트 폴더로 간주됩니다.

Exercise

앞서서 표준 입출력은 화면 출력뿐 아니라 다른 방법으로도 처리될 수 있다고 언급했습니다.
위에서 방금 만든 scanf 연습 프로그램을 가지고 테스트 해 보도록 하겠습니다.

프로그램이 있는 위치로 경로를 이동해서 입력파일과 출력파일을 지정했습니다.

  • 입력파일은 < 로, 출력파일은 > 로 표기합니다.

표준 입출력은 OS 에서 사용할 수 있는 여러 입출력장치를 통해 사용자에게 전해집니다.

이 블로그는 개인 블로그입니다. 게시글은 오류를 포함하고 있을 수 있지만, 저자는 오류를 해결하기 위해 노력하고 있습니다.
게시글에 별도의 고지가 없는 경우, 크리에이티브 커먼즈 저작자표시-비영리-변경금지 4.0 라이선스를 따릅니다.

This blog is personal blog. published posts may contain some errors, but author doing efforts to clear errors.
If post have not notice of license, it under creative commons Attribution-NonCommercial-NoDerivatives 4.0.