과학

자바로 프로그래밍에 입문할래요: 1.5. 입출력 (1)

입출력은 원래 3개로 나누었었는데, 두 개로 작성하려고.

눈으로 보는 사람들은 안 그래도 재미 없을 텐데 이건 재미 없는 부분들이 더 많은 것 같아서..

그래서 이번 글은 좀 길다.

 

이 절 이후부터는 프린스턴 대학교에서 제공하는 입출력 라이브러리만을 이용해서 프로그램을 작성하게 돼.

System.out.println()과는 영원히 안녕이고, 다음 글에서는 그림과 소리까지 함께 다뤄볼 거야. 이번 글은 텍스트만!

터미널을 많이 안 써본 사람들에게는 그래도 이번 글 후반부 내용이 꽤 흥미롭지 않을까 생각해.

 

1.4. 배열 (3)

1.4. 배열 (2)

1.4 배열 (1)

1.3. 조건문과 반복문 (2)

1.3. 조건문과 반복문 (1)

1.2. 내장 자료형 (2)

1.2. 내장 자료형 (1)

0.0. 여는 글, 1.1. 첫 프로그램 만들기

 

========================

 

입출력

  이번 차시에서는 표준 입력(standard input), 표준 그림(standard drawing), 표준 소리(standard audio) 등을 통해, 우리가 이미 자바 프로그램과 바깥 세상 사이에 사용했던 간단한 추상화(커맨드 라인 입력과 표준 출력)를 좀 더 확장해볼 것입니다.

 

  I/O는 input/output의 축약입니다. 이는 프로그램이 바깥 세상과 소통하는 방법에 대한 것입니다. 여러분의 컴퓨터 운영 체제가 컴퓨터 본체에 있는 물리적 기기와 소통하는 것이죠. 표준 입출력 추상화를 구현하기 위해서, 우리는 OS의 인터페이스인 라이브러리를 사용합니다. 이러한 표준 입출력 추상화를 사용하면, 좀 더 쉽게 여러분과 프로그램이 소통할 수 있습니다. 여러분이 하드 디스크의 물리적인 어느 부분에 접근할 건지, 키보드의 무슨 버튼을 눌렀는지, 어떤 화면에 출력할 건지를 일일이 신경쓴다면 머리 아프겠죠?

 

  여러분들은 이미 명령행 인자들을 통해 명령행의 값을 수용하고 문자열을 출력해보았습니다. 이번 차시의 목적은, 자료를 처리하고 제공하는데 사용되는 도구들을 좀 더 다양하게 보여주는 데 있습니다. 이제껏 사용해왔던 System.out.print()와 System.out.println() 메소드는 수학적 함수와는 거리가 멀죠. 반환값이 없기 때문입니다. 이들의 목적은 입출력 장치에 영향(side effect)을 일으키는 것입니다. 주로 장치를 통해 정보를 들여오거나, 내보내거나 하는 것이죠.

 

  표준 입출력 메커니즘의 필수적인 기능으로, 프로그램의 관점에서 입출력 양의 제한이 없어야 합니다. 여러분의 프로그램은 입력을 무한하게 소비하고, 출력을 무한하게 생성할 수 있습니다.

 

  흔한 표준 입출력 메커니즘으로, 여러분의 컴퓨터 디스크에 있는 파일과 프로그램을 연결하는 것이 있습니다. 쉽게 말해서 파일을 생성하고 읽는 것이죠. 이런 연결은 자바 프로그램으로 하여금 결과를 저장하거나 불러오고, 다른 프로그램들을 참조할 수 있게 합니다.

 

  

조감도

  크게 한 번 봐보죠. 자바 프로그램은 커맨드 라인으로부터 입력 값을 받고, 문자열을 출력합니다. 기본적으로, 커맨드 라인 입력과 표준 출력은 커맨드를 입력하는 응용 프로그램으로부터 연결 됩니다. 예를 들어, 여러분이 입력한 'java HelloWorld'와, 출력된 'Hello, World!'는, <HelloWorld>라는 프로그램이 입력과 출력을 연결해준 것입니다. 그리고 이런 식의 방법은 생각보다 편리하죠.

 

 

명령행 입력(Command-line input)

  우리가 프로그램에 입력값을 제공하기 위해 사용했던 이 메커니즘은 자바 프로그래밍의 표준입니다. 모든 클래스들은 main() 메소드가 있고, 인자로서 String 배열로 args[]를 받죠. args[] 배열은 우리가 입력한 명령행 인자들로 OS에게서 자바에게 제공됩니다. 편의 상 자바와 OS 둘 다 인자를 String으로 처리하므로, 숫자나 인자를 받고 싶다면 Integer.parseInt()나 Double.parseDouble() 메소드를 이용해 String에서 원하는 자료형으로 변환해야 합니다.

 

 

표준 출력(Standard output)

  프로그램에 출력을 찍기 위해, System.out.println()과 System.out.print()를 사용합니다. 자바는 이 메소드들에 의한 호출 시퀀스의 결과를, 추상화된 문자 스트림의 형태로 제공하게 됩니다. 이것이 표준 출력입니다. 기본적으로 OS는 표준 출력으로 터미널 윈도우와 연결하기 때문에, 자바 프로그램의 모든 출력은 터미널 윈도우에 나타나게 됩니다.

 

  우리의 위대한 시작점은 다음 프로그램부터입니다. <프로그램 1.5.1, RandomSeq>는 명령행 인자 N을 받아 0부터 1사이의 무작위 숫자를 N개 출력합니다. 이는 OS가 허용하는 한 우리가 무한한 출력을 생성할 수 있음을 보여줍니다.

 

 

프로그램 1.5.1: Generating a random sequence

 
public class RandomSeq
{
	public static void main(String[] args)
	{ 	// Print a random sequence of N real values in [0, 1)
		int N = Integer.parseInt(args[0]);
		for (int i = 0; i < N; i++)
			System.out.println(Math.random());
	}
}
% java RandomSeq 1000000
0.2498362534343327
0.5578468691774513
0.5702167639727175
0.32191774192688727
0.6865902823177537
...

 

  우리는 이제 우리에게 훨씬 더 유용한 프로그래밍의 모델을 제공하는 세 개의 추가적 메커니즘으로 명령행 입력의 한계를 해결할 것입니다. 

 

 

표준 입력(Standard input)

  클래스 StdIn은 표준 출력의 추상화를 보완하기 위해 표준 입력의 추상화를 구현하는 라이브러리입니다. 프로그램이 실행 중일 때에는 언제든지 표준 출력을 통해 값을 출력할 수 있는 것처럼, 언제든지 표준 입력 스트림을 통해 값을 읽을 수 있을 것입니다.

 

 

표준 그림(Standard drawing)

  클래스 StdDraw는 프로그램에 그림을 생성할 수 있게 합니다. 이는 점과 선으로 된 그림을 그릴 수 있게 해주는 간단한 그래픽 모델입니다. StdCraw는 문자, 그림, 애니메이션과 관련된 것들도 포함하고 있습니다.

 

 

표준 소리(Standard audio)

  클래스 StdAudio는 프로그램에 소리를 생성할 수 있게 합니다. 숫자 배열을 소리로 변환합니다.

 

 

  커맨드 라인 입력과 표준 출력을 사용하기 위해서는 자바 기본 기능을 사용합니다. 표준 입력, 표준 그림, 표준 소리와 같은 추상화들을 제공하는 자바 기본 기능들도 물론 있지만, 그것들은 여러분들이 사용하기 조금 복잡합니다. 따라서 우리는 StdIn, StdDraw, StdAudio 안에 있는 간단한 인터페이스를 통해서 접근할 것입니다.

 

  이는 프린스턴 대학교에서 제공하는 라이브러리들이며, 이를 CMD 혹은 Eclipse 터미널에서 사용하기 위해서는 다음 파일을 다운 받아 프로그램과 같은 경로에 설치해, 압축을 해제해야 합니다. 만약 Eclipse를 이용해서 강의를 진행하고 있다면, 그 다음 포스트 또한 참고하세요.

 

프린스턴 대학교에서 제공하는 표준 입출력 라이브러리

Eclipse에서 프로젝트에 외부 라이브러리(library) 추가하기

 

  참고로, 위 라이브러리는 default-package에서 작성된 라이브러리이므로 직접 클래스에서 사용할 때에도 해당 클래스가 어떤 패키지에도 속하지 않아야 합니다. 만약 해당 라이브러리의 클래스를 인식하지 못하는 문제가 발생한다면, 이를 확인해보세요.

 

 

표준 출력(Standard output)

  자바의 System.out.print()와 System.out.println() 메소드는 우리가 필요한 수준의 표준 출력 추상화를 구현합니다. 하지만 우리는 입문자이므로, 앞으로 사용될 다른 표준 출력들도 일관적인 방법으로 사용하고 싶습니다. 따라서 앞으로는 표준 입출력을 자바와 비슷하게 구현한 StdOut 라이브러리를 사용할 것입니다. System.out.print()와 System.out.println()은 대신 StdOut.print()와 StdOut.println()을 사용하면 됩니다.

 

 

  StdOut.printf() 메소드는 이 절의 주된 주제입니다. 이를 이용하면, 출력의 표현을 좀 더 제어할 수 있습니다. 이는 C언어의 기능을 따서 구현한 것인데, C언어가 1970년부터 여지껏 살아남을 수 있었던 이유는 여러 모로 유용했기 때문이죠.

 

  여러분은 double 값을 처음 출력해보았을 때, 지나치게 정확한 출력에 당황했을 것입니다. 예를 들어, System.out.print(Math.PI) 를 작성하면, 3.14를 훨씬 넘어 3.141592653589793까지 출력되죠. 

 

  printf() 메소드는 조금 더 유연합니다. 문자열로 데이터를 출력할 때, 몇자리까지 출력할 건지 명시할 수 있습니다. printf()로 3.14159를 출력하기 위해, 우리는 StdOut.printf("%7.5f", Math.PI) 처럼 작성할 수 있습니다. 또한 <프로그램 1.3.6>의 System.out.print(t)를 다음과 같이 바꿀 수 있습니다.

 

 
StdOut.printf("The square root of %.1f is %.6f", c, t);
The square root of 2.0 is 1.414214

 

 

  다음은, 이들의 의미와 각 내장 자료형에 따른 이 구문들의 확장된 연산들에 대해 알아보겠습니다.

 

포맷팅 기초(Formatted printing basics)

  가장 간단한 형식으로, printf()는 두 인자를 받습니다. 첫번째 인자는 포맷 스트링(형식 문자열)입니다. 포맷 스트링은, 두번째 인자를 문자열로 바꿀 때 어떻게 바꿀 것인지에 대해 정의합니다.

 

 

 

 

  간단한 포맷 스트링의 예로, %와 한 글자의 변환 지정자(Conversion specifiers)로 이루어진 것을 들 수 있습니다. 변환 지정자의 경우 자주 쓰이는 것으로는 d, f, s이며, 이는 각각 decimal(십진수의 정수), floating-point(부동소수점 수), string(문자열)을 의미합니다. 예를 들어 %d이면 해당하는 자리에는 정수가 문자열로 바뀔 것입니다.

 

  %와 변환 지정자 사이에 정수를 넣어 자릿수(field width)를 정할 수 있습니다. 만약 자릿수가 남는다면, 그만큼 왼쪽에 공백이 함께 출력됩니다. 만약 오른쪽에 공백을 추가하고 싶다면, 음수로 표기하면 됩니다. 만약 변환된 문자열이 자릿수보다 더 크다면, 자릿수는 무시됩니다.

 

  따라서 이를 통해 double값의 소수점 이후 자릿수를 어디까지 표기할 건지, 문자열의 경우 몇 번째 문자부터 몇 번째 문자까지 표기하는지 등을 정할 수 있습니다. 잊지 말아야할 사실은, printf에서 변환 지정자의 포맷과 해당하는 위치의 자료형이 반드시 일치해야 한다는 점입니다.

 

  

포맷 스트링(Format string)

  printf()의 첫번째 인자는 포맷 스트링을 포함한 문자열입니다. 포맷 스트링이 아닌 부분은 그대로 출력되며, 포맷 스트링인 부분은 인자들에 따라 문자열로 치환됩니다. 다음을 참고하세요. \n은 printf()에서 개행을 하게 하는 문자입니다.

 

 
StdOut.printf("PI is approximately %.2f\n", Math.PI);
PI is approximately 3.14

 

 

 

 

 

 

더 많은 인자

  printf() 함수는 둘을 초과하는 인자들도 받을 수 있습니다. 각 형식 지정자가 인자들에 일대일 대응합니다. 예를 들어, 대출 계좌를 관리하는 코드를 작성한다면, 반복문 안에 다음 구문이 있을 것입니다. 

 

 
String formats = "%3s  $%6.2f   $%7.2f   $%5.2f\n";
StdOut.printf(formats, month[i], pay, balance, interst);
    payment   balance   interest
Jan 9.00   42.67   .67
Feb 9.00   84.26   .59
Mar 9.00   24.78   .52

 

  여러 문자열을 다룰 때, 포맷 스트링은 꽤 편리합니다.

 

 

표준 입력(Standard input)

  우리가 제공하는 StdIn 라이브러리는 표준 입력 스트림으로부터 공백으로 구분된 일련의 자료들을 받아들입니다. 각 값은 String이거나, 자바의 기본 자료형 중 하나입니다. 표준 입력 스트림의 주요한 기능 중 하나는, 여러분의 프로그램이 입력을 받을 때 해당하는 입력을 소모해버린다는 것입니다. 한 번 읽었다면, 다시 읽을 수 없습니다.

 

  라이브러리는 다음 주요한 메소드들을 갖고 있습니다: isEmpty(), readInt(), readDouble(), readLong(), readBoolean(), readChar(), readString(), readLine(), readAll(). 

 

 

 

 

 

입력하기

  터미널에서 java 커맨드를 사용할 때,(아마 여러분들은 IDE를 사용하느라 java 커맨드를 입력해볼 기회는 별로 없었을 것입니다-옮긴이) 여러분들은 실제로는 세 가지의 일을 하고 있는 것입니다: 여러분들의 프로그램을 실행시키도록 하고, 명령행 인자를 명시하며, 표준 입력 스트림을 정의합니다. 여러분이 커맨드를 입력하고, 이후에 터미널 윈도우에 입력되는 문자열들은 표준 입력 스트림입니다. 무언가를 입력하면, 프로그램과 상호작용 할 수 있습니다.

 

  프로그램은 여러분이 표준 입력 스트림을 생성할 때까지 기다립니다. 예를 들어, 다음 프로그램을 실행하면, 명령행 인자로 N을 받고나서 N개의 숫자를 읽어들여 그들을 더하게 됩니다.

 

 
public class AddInts 
{
    public static void main(String[] args) 
    {
	int N = Integer.parseInt(args[0]);
	int sum = 0;
	for (int i = 0; i < N; i++) {
	    int value = StdIn.readInt();
	    sum += value;
	}
	StdOut.println("Sum is " + sum);
    }
}

 

  여러분이 터미널에 'java AddInts 4'라고 타이핑했을 때, 프로그램은 StdIn.readInt() 메소드를 호출하여 여러분의 정수 입력을 기다리게 됩니다. 만약 여러분이 144를 입력하고 싶다고 해보죠. 1, 4, 4를 차례로 입력하지만, 그리고는 아무런 일도 일어나지 않습니다. 왜냐하면 프로그램은 여러분이 입력을 끝마쳤는지 아닌지를 확신할 수 없기 때문이죠. 따라서 <return>(엔터)을 입력하면, 비로소 프로그램은 하나의 정수를 읽어들이게 됩니다. 

 

 

입력 형식(Input format)

  StdIn.readInt() 가 정수를 기대하고 있는데 여러분이 abc, 12.2, true와 같이 입력한다면, NumberFormatException이 발생할 것입니다. 언제나 해당하는 포맷의 자바 리터럴로 입력해주어야 합니다. 편의를 위해 StdIn은 공백으로 각 입력을 구분할 수 있습니다. 숫자들 사이에 공백이 몇 칸이나 있는지는 상관 없습니다. 

 

 

 대화형 입력(Interactive user input)

  <프로그램 1.5.2, TwentyQuestions>는 사용자와 상호작용하는 프로그램의 간단한 예입니다. 프로그램은 난수를 생성하며, 사용자에게 숫자를 추측할 수 있게끔 단서를 던져줍니다.(참고: 이진 탐색을 이용하면 여러분은 언제나 숫자를 맞출 수 있습니다) 여지껏 작성한 다른 프로그램들과의 근본적 차이는, 사용자가 프로그램 실행 도중에 제어 흐름을 바꿀 수 있다는 것입니다. 이것은 여러분들이 보기에도 굉장히 중요해보일 것입니다. 현대의 많은 프로그램들은 GUI(graphical user interface)를 통해 다양한 입력들을 받아 제어 흐름을 변경합니다.

 

 

프로그램 1.5.2: Interactive user input

 
public class TwentyQuestions
{
	public static void main(String[] args)
	{	// Generate a number and answer questions
		// while the user tries to gues the value.
		int N = 1 + (int) (Math.random() * 1000000);
		StdOut.print("I'm thinking of a number ");
		StdOut.println("between 1 and 1,000,000");
		int m = 0;
		while (m != N)
		{ // Solicit one guess and provide one answer
			StdOut.print("What's your guess? ");
			m = StdIn.readInt();
			if (m == N)
				StdOut.println("You win!");
			if (m < N)
				StdOut.println("Too low ");
			if (m > N)
				StdOut.println("Too high");
		}
	}
}
% java TwentyQuestions
I'm thinking of a number between 1 and 1,000,000
Whats' your guess? 500000
Too low 
What's your guess? 750000
Too low
Whats' your guess? 875000
Too high
...

 

  <프로그램 1.5.2>는 간단한 high/low 게임입니다. 여러분이 입력하는 숫자가 맞는지 확인할 수 있으며, 만약 틀리다면 여러분의 숫자가 낮은지, 높은지 알려줍니다. 

 

 

프로그램 1.5.3: Averaging a stream of numbers 

 
public class Average
{
	public static void main(String[] args)
	{ 	// Average the numbers on the input stream.
		double sum = 0.0;
		int cnt = 0;
		while (!StdIn.isEmpty())
		{ // Read a number and cumulate the sum.
			double value = StdIn.readDouble();
			sum += value;
			cnt++;
		}
		double average = sum / cnt;
		StdOut.println("Average is " + average);
	}
}
% java Average
10.0 5.0 6.0
3.0
7.0 32.0
<ctrl-z>
Average is 10.5
% java RandomSeq 100000 > data.txt
% java Average < data.txt
Average is 0.5010473676174824

% java RandomSeq 100000 | java Average
Average is 0.5000499417963857

 

  <프로그램 1.5.3>은 일련의 실수들을 받아 평균을 출력합니다. 입력 스트림의 크기에는 제한이 (오버플로우가 일어나지 않는 한에서)없습니다. 오른쪽의 명령에 왼쪽의 명령을 연결(piping)하는 모습도 볼 수 있습니다. 보통 <ctrl-z> 혹은 <ctrl-d>를 통해 콘솔에게 입력의 끝을 알릴 수 있습니다. 제대로 작동하지 않는다면, 다음 포스팅을 참고하세요.

 

[Eclipse] 콘솔에서 Ctrl+Z를 통한 EOF 입력이 되지 않을 때 해결 방법

 

 

임의의 크기의 입력 스트림을 처리하기

  보통 우리가 입력을 무한하게 하지는 않습니다: 여러분의 프로그램이 입력 스트림을 통해 스트림이 텅 빌 때까지 값을 소모합니다. 하지만 입력 스트림의 크기에 대한 제한은 없으며, 몇몇 프로그램은 이것만으로도 제공되는 입력들을 전부 처리할 수 있습니다.

 

  <Average>는 표준 입력을 통해 읽은 일련의 실수들의 평균을 출력합니다. 이는 입력 스트림을 사용할 때의 중요한 속성을 드러내는데, 스트림의 길이를 프로그램은 알 수 없다는 것입니다. 숫자를 읽기 전에 프로그램은 StdIn.isEmpty() 메소드를 사용해 입력 스트림에 아직 입력이 남아있는지를 확인합니다. 어떻게 더 이상 데이터가 없음을 확인할 수 있을까요?

 

  관습 상, 특별한 일련의 문자들을 이용합니다. 이를 EOF(end-of-file)이라고 합니다. 안타깝게도, 우리가 현대의 운영체제에서 접하는 터미널 응용 프로그램들은 서로 EOF의 관습이 살짝씩 다릅니다. 이 강의에서는 <ctrl-z>를 사용했고, 다른 많은 시스템에서는 <ctrl-d>도 사용 합니다. 

 

  <Average>는 간단한 프로그램이지만, 프로그래밍의 새롭고 엄청난 능력을 재현합니다: 표준 입력이 있다면, 무한한 양의 데이터들을 처리하는 프로그램을 작성할 수 있다는 것입니다. 

 

  표준 입력은 우리가 사용해왔던 명령행 입력 모델에 있어 중요한 단계입니다. 다음 두 가지를 알 수 있게 되기 때문이죠. 첫째, <TwentyQuestions>와 같이 우리가 프로그램과 상호작용 할 수 있습니다. 둘째, <Average>와 같이 우리가 굉장히 많은 양의 데이터를 읽어들일 수 있습니다.

 

 

리디렉션과 파이프(Redirection and piping)

  많은 응용프로그램에서, 표준 입력에 데이터를 직접 손으로 입력하는 것은 별로 권고되지는 않습니다. 우리가 타이핑 할 수 있는 양의 데이터까지만 프로그램의 능력이 발휘될테니까요.(심지어 타이핑 속도도 영향을 받겠죠) 비슷한 이야기로, 우리가 어떤 결과를 보는데 항상 자바 프로그램을 실행시켜 터미널에 찍힌 표준 출력을 통해서만 볼 수 있다면 이것 또한 효율적이지 않습니다. 따로 어딘가 결과를 저장해두고 싶죠.

 

  이런 문제들을 해결하기 위해 우리가 집중할 생각은, 표준 입력은 추상화 되어 있다는 것입니다. 추상화 되어 있다는 것은 우리의 프로그램이 단순히 입력 스트림을 통해 입력을 받을 뿐 입력 스트림이 어떻게 생겼는지, 어떤 키보드를 통해서 입력을 받는지, 어떤 코드들로 구성되어 있는지는 별로 신경쓸 필요가 없게 설계가 되어 있다는 것입니다. 마찬가지로 표준 출력도 추상화 되어 있습니다.

 

  이런 추상화의 힘은 우리에게끔 표준 입출력을 위한 다양한 다른 소스들을 명시할 수 있게 합니다. 예를 들어, 파일, 네트워크, 혹은 다른 프로그램으로부터 입력을 받을 수 있게 하는 것입니다. 모든 현대의 운영체제는 이런 메커니즘들을 구현합니다.

 

 

표준 출력에서 파일로 리디렉션 하기

  영구적으로 저장하거나 다른 프로그램의 입력으로 사용하기 위해, 간단한 커맨드를 추가하여 프로그램 실행 시 표준 출력을 파일 생성으로 리디렉션(우회)시킬 수 있습니다. 다음 명령어는 <RandomSeq>에 인자를 1000으로 넘겨주며 실행시키고, 해당 출력을 <data.txt> 파일로 리디렉션하는 명령어입니다. 이는 운영체제에서 지원해주는 것이므로, Eclipse를 활용하기 위해서는 Eclipse 내부의 Terminal을 실행시켜서 입력해야 합니다. 

 

 
% java RandomSeq 1000 > data.txt

 

 

  위 명령어를 사용 시, 표준 출력 스트림이 터미널 윈도우에 출력이 되는 것이 아닌 <data.txt>라는 이름의 파일에 작성될 것입니다. System.out.print()나 System.out.println()을 호출할 때마다, 파일의 끝부분에 해당 문자열이 계속 추가될 것입니다. 이번 경우, 1000개의 무작위 값이 적혀있겠네요.

 

 

파일에서 표준 입력으로 리디렉션 하기

  비슷하게, 우리는 표준 입력을 리디렉션 할 수 있습니다. 다음 명령어를 사용하면, StdIn은 자료를 터미널이 아닌 파일로부터 읽게 될 것입니다.

 

 
% java Average < data.txt

 

  이 커맨드는 <data.txt>에 있는 일련의 숫자들을 읽게 합니다. 그리고 그들의 평균을 계산하죠. 특히, < 기호는 운영체제에게 표준 입력 스트림을 터미널 윈도우에서 사용자의 타이핑을 기다리는 것 대신 <data.txt>로부터 읽게 만듭니다. 프로그램이 StdIn.readDouble()을 호출할 때마다, 운영체제는 파일로부터 그 값을 읽어들입니다. 

 

 

두 프로그램을 연결하기

  더욱더 유연하게 표준 입력과 표준 출력 추상화를 구현하는 방법은, 자체 프로그램에 의해 구현되게 만드는 방법입니다. 이 메커니즘을 piping(파이프)라고 합니다. 다음 명령어는 <RandomSeq>의 출력을 <Average>의 입력으로 연결시킵니다.

 

 
% java RandomSeq 1000 | java Average

 

  이는 <RandomSeq>의 표준 출력 스트림이 <Average>의 표준 입력 스트림과 같다고 명시하는 것입니다. 이렇게 하면, <Average>가 동작하는 동안, <RandomSeq>가 무작위의 수를 생성해 입력시켜줍니다. 이는 다음 명령어들을 사용하는 것과 동일한 효과를 가져옵니다.

 

 
% java RandomSeq 1000 > data.txt
% java Average < data.txt

 

  물론 <data.txt> 파일이 생성되지 않는다는 점에서 차이는 있습니다. 이는 중요한 차이점인데, <data.txt> 파일이 생성될 디스크 공간이 필요하지 않다는 것입니다. 예를 들어 우리는 1000 대신 1000000000을 넣어서, 10억 개의 숫자를 생성해내도 10억 개의 숫자가 저장될 공간을 마련할 필요가 없습니다.

 

  <RandomSeq>가 System.out.println()을 호출하여 문자열을 출력할 때, <Average>는 StdIn.readInt()를 호출하여 해당 출력을 입력으로 받아들이고, 후에 문자열은 삭제됩니다. 이런 일이 언제 어떻게 일어나느냐 하는 것은 글쎄요. 아마 여러분의 PC에 있는 운영체제만이 알 것입니다. 

 

 

프로그램 1.5.4: A Simple filter

 
public class RangeFilter
{
	public static void main(String[] args)
	{ 	// Filter out numbers not between lo and hi.
		int lo = Integer.parseInt(args[0]);
		int hi = Integer.parseInt(args[1]);
		while (!StdIn.isEmpty())
		{ 	// Process one number,
			int t = StdIn.readInt();
			if (t >= lo && t <= hi)
				StdOut.print(t + " ");
		}
		StdOut.println();
	}
}
% java RangeFilter 100 400
358 1330 55 165 689 1014 3066 387 575 843 203 48 292 877 65 998
358 165 387 203 292
<ctrl-z>

 

 

필터(Filters)

  파이프는 1970년 초 UNIX 시스템의 핵심 기능이었습니다. 현대 시스템에도 여전히 건재한데, 간단한 추상화로 서로 다른 프로그램들을 연결할 수 있기 때문입니다. 많은 UNIX 프로그램들이 당시 프로그래머들의 예상보다 훨씬 더 큰 크기의 파일들을 처리하는데도 여전히 문제가 없다는 것은, 이 추상화가 굉장히 강력하다고 볼 수 있죠.

 

  자바 프로그램들끼리는 메소드 호출로 의사소통 할 수 있지만, 표준 입출력은 서로 다른 시간에 쓰여진 프로그램들, 아마 자바 언어가 아닌 다른 언어로 쓰여있는 프로그램들과 의사소통 해야할 것입니다. 표준 입출력은 바깥 세계와 소통하는 간단한 인터페이스의 역할을 하는 것입니다.

 

  쉽게 이야기 하면, 각 프로그램들은 일종의 필터인 셈입니다. 표준 입력을 받아들여, 표준 출력으로 바꾸는 필터죠. 이 개념을 잘 받아들이세요. 프로그램은 일련의 입력을 받아 그 입력에 맞는 출력을 내보내는 것 그 이상 그 이하도 아닙니다.

 

  UNIX에서 고안된 몇몇 표준 필터들은 여전히 현대 운영체제에서 명령어로 사용되고 있습니다. 예를 들어, <sort> 필터는 표준 입력을 받아 정렬된 상태로 출력해줍니다. 

 

 
% java RandomSeq 6 | sort
0.035813305516568916
0.14306638757584322
0.348259347192635249
0.5765252840139256
0.72357518425267216
0.97953764516572647

 

  두 번째 유용한 필터는 <grep>입니다. <grep>은 입력을 받아, 주어진 패턴이 있는 문장들을 출력해줍니다. 예를 들어 다음 명령어는 소스 파일 <RangeFilter.java>에서 'lo'가 포함된 문장들을 출력해주는 명령어입니다. 윈도우 10에서는 이런 명령어가 유효하지 않으며, 대신 <findstr> 명령어를 사용할 수 있습니다.

 

 
% grep lo < RangeFilter.java
	{ 	// Filter out numbers not between lo and hi.
		int lo = Integer.parseInt(args[0]);
			if (t >= lo && t <= hi)

 
% findstr "lo" < RangeFilter.java
	{ 	// Filter out numbers not between lo and hi.
		int lo = Integer.parseInt(args[0]);
			if (t >= lo && t <= hi)

 

  프로그래머들은 종종 변수의 이름이나 언어의 어떤 구문의 사용 사례를 보고 싶을 때, grep 명령어를 이용해 확인합니다. 세 번째 필터는 <more>입니다. <more>은 수많은 데이터들이 출력될 때, 너무 많이 출력 되어 초반에 출력된 데이터들을 확인하기 힘들 때 사용합니다. 출력될 데이터들이 아무리 많아도 터미널 화면에 보이는만큼만 출력되며, 스페이스 바를 눌러 남은 출력들을 계속 볼 수 있습니다.

 

계속.

1개의 댓글

2021.05.13

c#공부하는데 참조 많이 하고 갑미다

0
무분별한 사용은 차단될 수 있습니다.
번호 제목 글쓴이 추천 수 날짜
501 [과학] 자바로 프로그래밍에 입문할래요: 2.2. 라이브러리와 클라이... 4 스비니 7 4 일 전
500 [과학] 아무도 알고싶지 않을수도 있는 이야기 - NASA의 인증을 받은... 26 Te2t1c1e 14 7 일 전
499 [과학] 자바로 프로그래밍에 입문할래요: 2.1. 정적 메소드 (2) 8 스비니 0 12 일 전
498 [과학] 암흑에너지는 존재하는가? 19 월급받으며개드립하기 8 20 일 전
497 [과학] 자아도취에 빠져버린 오늘날의 과학계 9 위너스리그 11 20 일 전
496 [과학] 자바로 프로그래밍에 입문할래요: 2.1. 정적 메소드 (1) 4 스비니 2 21 일 전
495 [과학] 비트코인/블록체인 개념 안되는사람들을 위한 읽을거리 13 냥봉이 1 21 일 전
494 [과학] 자바로 프로그래밍에 입문할래요: 1.5. 입출력 (2) 16 스비니 3 2021.05.18
493 [과학] 자바로 프로그래밍에 입문할래요: 1.5. 입출력 (1) 1 스비니 2 2021.05.09
492 [과학] 자바로 프로그래밍에 입문할래요: 1.4. 배열 (3) 1 스비니 4 2021.05.01
491 [과학] 일론 머스크는 스티브 잡스 같은 사람이다. (feat. 뉴럴링크) 38 빨머 20 2021.04.29
490 [과학] 자바로 프로그래밍에 입문할래요: 1.4. 배열 (2) 9 스비니 2 2021.04.23
489 [과학] 방사선에 관하여_20210423_Ch.4까지 완료 63 ptrtype01 4 2021.04.22
488 [과학] 日 자민당 의원이 공개한 원전오염수 성분 31 SOLEUS 16 2021.04.20
487 [과학] ??? : 한국이 오염수 더 많이 방류한다 빼애액 24 SOLEUS 13 2021.04.20
486 [과학] 일본의 방사능 기준치가 신용받지 못하는 이유 71 뭘까요 1 2021.04.19
485 [과학] 도쿄의 암 발생률. 27 뭘까요 5 2021.04.19
484 [과학] 일본은 과연 후쿠시마 인근만 방사능에 오염돼었을까? 13 뭘까요 10 2021.04.19
483 [과학] 일본이 주장하는 후쿠시마의 방사능량은 서울과 같다는게 사... 31 뭘까요 14 2021.04.19
482 [과학] 일본 후쿠시마 오염수 방류 팩트정리 21 뭘까요 7 2021.04.19