Tuesday, September 18, 2012

표준 입력에서 읽어오기(Reading from the Standard Input)

프로그램에서 읽어 들이는 모든 데이터는 먼저 변수로 저장 되어야 합니다. 예를 들자면, 입력에서 학생 수를 읽어 들이는 프로그램은 먼저 학생수의 정보를 변수로 저장해야 합니다. 이때 사용할 수 있는 변수의 종류는 int가 적절합니다.

이전 챕터에서 살펴 보았듯이, 결과물을 출력 할 때 stdout매번 입력 할 필요는 없습니다. 왜냐하면 출력 함수라는 이름 자체에서 뿐만 아니라 출력 인자를 받는 다는 자체에서 stdout(표준 출력)를 암시하고 있기 때문입니다.
그러므로 stdin을 생략하고 write(studentCount)사용하는 것 만으로도 변수 studentCount 출력하기에는 충분합니다. 정리 하자면:
스트림(흐름): stdout(표준 출력)
연산: write
데이터: studentCount(학생수)를 나타내는 변수
출력 타겟: 일반적으로 콘솔창
write의 반대 개념은 readf입니다; 표준 입력으로 부터 데이터를 읽어 오는 역할을 합니다. 이름에 끼여 있는 f는 "formatted"(형식을 가진, 포멧된)의 약자로써, 읽어올 대상은 항상 특정한 포멧을 가져야 합니다.
(역자 주: 포멧을 가진다는 것은 특정한 형식을 가져야 한다는 의미입니다. 예를 들어 집주소를 떠올려 봅시다. 서울 특별시 종로구~어쩌구 저쩌구는 주소의 형식, 즉 주소의 포멧을 가지고 있습니다. 인터넷 주소도 http://p-crimsonsphere.blogger.com 이라는 형식, 즉 인터넷 주소의 포멧을 가지고 있습니다. 프로그래밍 언어에서도 포멧된 데이터라고 말한다면 특정 형식을 갖추고 있는 데이터라고 이해 하시면 됩니다.)

앞선 챕터에서 표준 입력의 스트림은 stdin이라는 것을 공부 하였습니다.
데이터를 읽어 오는 경우를 생각 해 볼 경우, 그럼 읽어온 데이터는 어디에 저장 해야 할까요? 정리하자면:
스트림: stdin(표준 입력)
연산: readf
데이터: 어떠한 정보
타겟: ?
읽어온 정보를 저장할 위치는 변수의 주소(어드레스)로 지정합니다. D언어에서 변수 앞에 "&"문자를 붙이면 그 변수가 가진 주소를 지칭하게 됩니다. 예를 들면, 변수 studentCount의 주소는 &studentCount가 됩니다. 여기서 &studentCount는 "studentCount의 주소"라고 읽을 수 있으며, 위 도표의 ?로 빠져 있는 퍼즐 조각의 하나가 됩니다.

스트림: stdin(표준 입력)
연산: readf
데이터: 어떠한 정보
타겟: studentCount의 주소
변수 앞에다가 &를 붙이는 것은 그 변수가 실제로 지칭(pointing) 하고 있는 것을 나타냅니다.
(역자 주: 프로그래밍을 입문 할 때 가장 처음 부딪히는 관문이 포인터죠. 여기서는 그러려니 넘어가세요.) 이 개념은 후에 공부 하게 될 참조(reference)와 포인터(pointer)의 기초가 됩니다.

readf를 사용할때 주목해야 할 사항이 한가지 있지만 나중에 설명 하겠습니다; 지금은, readf의 첫번째 인자는 "%s"가 되어야 한다는 점을 기억합시다. 
    readf("%s", &studentCount);

참고: 밑에서 설명 하겠지만, 대부분의 경우 스페이스도 한칸 넣어 줍니다: " %s"

" %s" 입력받은 데이터가 자동적으로 변수의 형태에 맞도록 변환해 주는 역할을 합니다. 예를 들면 '4'와 '2'라는 문자를 입력으로 받아서 int타입의 변수에 저장한다면, 이렇게 받은 문자열은 자동적으로 정수인 42로 변경 됩니다.

아래의 프로그램은 사용자에게 학생 수를 입력하도록 요청합니다. 숫자를 모두 입력 하면 엔터를 누릅니다:
import std.stdio;

void main()
{
    write("How many students are there? ");

    /*
     * The definition of the variable that will be used to
     * store the information that is read from the input.
     */
    int studentCount;

    // Storing the input data to that variable
    readf("%s", &studentCount);

    writeln("Got it: There are ", studentCount, " students.");
}

공백 문자 제거하기

데이터를 입력 한 후에 누르는 엔터 키 또한 stdin에서는 특수문자로 인식하여 저장하게 됩니다. 이러한 엔터키는 정보가 한줄인지, 여러줄인지를 구분할 때 유용합니다.

이와 같이 때로는 유용하지만, 이러한 특수문자는 대부분의 경우 불필요하며 실제로는 입력에서는 배제되어야 합니다. 그렇지 않으면 입력을 완료 할 수 없기 때문에 다른 데이터로부터 입력을 받는 것이 불가능 해지겠죠.

이러한 문제점을 살펴보기 위해서 교사의 수도 입력으로 받아 봅시다.
import std.stdio;

void main()
{
    write("How many students are there? ");
    int studentCount;
    readf("%s", &studentCount);

    write("How many teachers are there? ");
    int teacherCount;
    readf("%s", &teacherCount);

    writeln("Got it: There are ", studentCount, " students",
            " and ", teacherCount, " teachers.");
}
안타깝게도, 두번째 int를 읽을 후에는 프로그램을 더 이상 진행 할 수 없게 됩니다.
How many students are there? 100
How many teachers are there? 20
  ← The program gets stuck here
사용자가 교사의 수로 20을 입력 하였다고 하더라도, 100을 입력 하기 전에 입력된 특수문자 엔터가 입력 스트림에 잔존하여 프로그램의 진행을 가로막게 됩니다. 입력 스트림에서의 문자들을 다음과 같이 표현 해 본다면 명확하게 문제를 알 수 있습니다:
100[EnterCode]20[EnterCode]
입력을 가로막는 엔터는 붉은색으로 표시 했습니다.

이 문제를 해결하려면 " %s" 앞에 공백 문자를 하나 삽입 하여 교사의 수를 세기 직전의 엔터는 중요하지 않다는 것을 표시하면 됩니다: " %s". 문자열 포멧에 삽입하는 공백은 0이라거나 보이지 않는 무의미한 문자를 입력으로부터 읽을때 무시해라는 명령을 내릴때 사용합니다. 이러한 문자에는 앞에서 설명 하였듯 실제 공백(Space)이거나 엔터(Enter)라거나 탭키(Tab) 등을 포함합니다. 이런 문자들은 공백 문자라고 불립니다.

일반적으로 " %s"는 입력에서부터 데이터를 읽어들일때 자주 볼 수 있습니다. 위 프로그램을 아래와 같이 고치면 바라는 결과를 얻을 수 있습니다:
// ...
    readf(" %s", &studentCount);
// ...
    readf(" %s", &teacherCount);
// ...
출력 결과:
How many students are there? 100
How many teachers are there? 20
Got it: There are 100 students and 20 teachers.

추가정보


  • 단 한줄을 주석 처리 하려면 //를 줄 앞에 입력합니다. 여러줄을 주석 처리 하려면 주석 처리할 맨 첫줄의 맨 앞에 /*, 그리고 맨 마지막에 */을 추가합니다.

    주석을 한번 더 주석 처리 하려면 /++/를 사용합니다:
        /+
         // A single line of comment
    
         /*
           A comment that spans
           multiple lines
          */
    
          A comment block that includes other comments
         +/
    

  • 코드의 공백은 대부분 필요 없습니다. 하지만 코드는 가능한 여러줄로, 공백을 활용하여 가능한한 읽기 좋은 문장으로 제작 하는것이 좋습니다. 하지만 문법을 제대로 파악 할 수만 있다면 다음과 가이 공백 없이 프로그램을 짜는것도 가능합니다:

    import std.stdio; void main() {writeln("Hard to read!");}
공백을 넣지 않으면 코드를 읽기 힘듭니다.

연습문제

  1. 유로화-달러화의 환률(1유로는 1.4 달러라고 가정합시다.), 그리고 돈을 유로화로 입력받아서 달러화로 환전 해 주는 프로그램을 만들어 봅시다.
  2. 정수를 입력받는 프로그램을 제작하여 숫자가 아닌 수를 입력하여, 프로그램이 제대로 동작하지 않도록 유도 해 봅시다.

No comments:

Post a Comment