Java

I/O, 표준 입출력

개발자공부 2024. 5. 20. 15:46

자바 I/O

Input(입력)과 Output(출력) 약자이다. 데이터를 읽고 쓰는 기능을 말하며, 파일, 네트워크, 다른 프로그램 등 다양한 소스와 대상으로부터 데이터를 주고받는데 사용된다.

 

Stream 스트림

스트림은 데이터를 순차적으로 처리하는 일련의 연속된 데이터 요소들입니다. 자바에서 스트림은 데이터의 흐름을 읽고 쓰는데 사용되는 추상적인 개념이다. 입출력 작업을 수행할 때 데이터를 한 곳에서 다른 곳으로 이동시키는 매개체 역할을 한다.

스트림은 두 가지 맥락에서 사용된다. 하나는 I/O에 관련된 데이터 흐름을 처리하는 것이다. 다른 하나는 자바 8 이후에 도입된 java.util.stream 패키지에 포함된 스트림 API이다.

 

 

java.io 패키지

자바 I/O 스트림 API에는 실제로 여러 인터페이스와 추상 클래스가 정의되어 있다.

 


 

바이트 기반 스트림

InputStream / OutputStream

해당 클래스들은 데이터를 바이트 단위로 읽고 쓰는 기본적인 기능을 제공한다.

ex ) FileInputStream, FileOutputStream 파일로부터 바이트를 읽거나 파일에 바이트를 쓰는데 사용

 

문자 기반 스트림

Reader / Writer

바이트 기반 스트림과 달리 문자 기반 스트림은 문자 데이터의 입출력을 처리한다. 인코딩 문제를 자동으로 처리해주기 때문에 텍스트 데이터를 다룰 때 특히 유용하다.

ex ) FileReader, FileWriter 텍스트 파일을 읽고 쓸 때 사용된다.

 

버퍼링 스트림

BufferedInputStream / BufferedOutputStream / BufferedReader / BufferedWriter

입출력 효율을 증가시키기 위해 내부적으로 버퍼를 사용한다. 버퍼링 스트림은 데이터를 일정량 모아 놓았다가 한 번에 처리하기 때문에 크기가 적은 입출력 연산이 많이 필요할 때 성능을 향상시킬 수 있다.

 

고수준의 스트림

DataInputStream / DataOutputStream 

자바의 기본 데이터 타입을 쉽게 읽고 쓸 수 있게 해줍니다. 예를 들어 정수, 실수 등을 데이터 소스로부터 직접 읽거나 쓸 수 있다.

ObjectInputStream / ObjectOutputStream

객체 직렬화에 사용되며, 객체를 바이트 형태로 변환하거나 바이트에서 객체를 복원하는 기능을 제공한다.

 


 

스트림 두 가지 주요 특성

단방향성

스트림은 단방향으로만 데이터를 처리한다. 즉 입력 스트림은 데이터를 읽는데 사용되며, 출력 스트림은 데이터를 쓰는 데 사용된다. 두 작업을 동시에 수행하려면 입력 스트림과 출력 스트림을 별도로 사용해야 한다.

 

순차적 접근

스트림은 데이터에 순차적으로 접근한다. 이는 스트림을 통해 데이터를 처리할 때 한 번에 하나의 데이터 요소만 처리하며 일반적으로 한 번 처리된 데이터 요소는 다시 되돌아갈 수 없다는 의미이다. (일부 스트림은 마킹 기능을 제공하여 이러한 제한을 일부 완화한다.)

즉 스트림은 먼저 보낸 데이터를 먼저 받는다. 또한 중간에 건너뜀 없이 연속적으로 데이터를 주고 받는다. 이 특성은 큐(Queue)의 FIFO(First In First Out)구조와 유사하다.

 


 

표준 입출력

자바에서 표준 입출력은 프로그램과 사용자 간의 기본적인 데이터 교환 방법을 제공한다.

 

표준 입출력 핵심 요소

System.in

System.in은 자바 프로그램의 표준 입력 스트림이다. 기본적으로 이 스트림은 키보드 입력을 받는다. 이는 InputStream 객체로 바이트 기반에 수준 낮은 입력 기능을 제공한다. 일반적으로 사용자로부터 텍스트를 읽기 위해 Scanner 클래스나 BufferedReader과 같은 더 수준 높은 래퍼(wrapper)를 사용하여 쉽게 데이터를 처리할 수 있다.

 

System.out

System.out는 자바 프로그램의 표준 출력 스트림이다. 텍스트 데이터를 콘솔에 출력한다. 이 스트림은 PrintStream 객체로 println(), print(), printf() 등 여러 메소드를 제공하여 다양한 방식으로 데이터를 콘솔에 출력할 수 있다.

 

System.err

System.err도 PrintStream객체이다.  주로 에러 메시지나 중요한 경고 메시지를 콘솔에 출력할 때 사용된다. System.out과 기능적으로 동일하지만 일반 출력과 에러 출력을 구분하여 사용자에게 보여주는 데 도움을 준다.

 

더보기
import java.io.IOException;

public class MySystemIn {

	public static void main(String[] args) {

		// 키보드에서 데이터를 받아 응용 프로그램 안으로 넣기
		System.out.println("알파벳 하나를 쓰고 Enter를 누르세요");

		// 바이트 기반으로 동작 --> 바이트는 0과 1로 구성
		// 정수요소 - byte, short, int, long
		int i;
		
		// 한 바이트씩 키보드에 값을 읽어라
		try {
			// 97 != 13 ---> true (13은 enter키이다.)
			while ((i = System.in.read()) != '\n') {
				System.out.println("i : " + i);
				System.out.println("(char)i : " + (char) i);
			}

			i = System.in.read();
			System.out.println("---------------");

			// 출력
			System.out.println("i : " + i);

			// 문자로 변환하고 싶다면 --> 컴퓨터 안에 있는 문자표를 활용
			System.out.println((char) i);
		} catch (IOException e) {
			e.printStackTrace();
		}

	} // end of main

} // end of class
더보기
import java.io.IOException;

public class MySystemIn2 {

	public static void main(String[] args) {
		
		// 키보드에서 데이터를 응용 프로그램 안으로 넣기 
		System.out.println("알파벳 하나를 쓰고 Enter를 누르세요");
		// 바이트 동작 --> 0과1로 구성 
		// 정수 -> byte, short, int, long
		
		int i; 
		// 한 바이트씩 키보드에 값을 읽어라 
		
		try {
			//          97    !=     13 --> true
			while( ( i = System.in.read() ) != '\n' ) {
				System.out.println(" i : " + i);
				System.out.println(" (char)i : " + (char)i);
			}
			
			
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		
		

	} // end of main 

}

 


 

기반 스트림(Base Stream)

기반 스트림은 데이터 소스와 직접 연결되는 스트림이다. 실제 데이터 읽기나 쓰기 작업을 수행한다. 이 스트림들은 데이터의 실제 입력 소스 (파일, 메모리, 네트워크 연결 등)에 직접 연결되어 데이터를 바이트 단위로 읽거나 쓰는 기본적인 기능을 제공한다.

 바이트 기반 스트림 : FileInputStream, FileOutputStream, SocketInputStream, SocketOutputStream 등이 있다. 이들은 바이트 데이터를 처리한다.

■ 문자 기반 스트림 : FileReader, FileWriter 등이 있다. 문자 데이터를 처리한다.

 

보조 스트림(Filter Stream)

보조 스트림은 기반 스트림 또는 다른 보조 스트림에 연결되어 사용된다. 이 스트림들은 데이터를 변환하거나 추가 기능을 제공하거나 성능을 향상시키는 역할을 한다. 보조 스트림은 자체적으로 데이터 소스에 연결되지 않으며 항상 다른 스트림을 필요로 한다.

■ 버퍼링 : BufferedInputStream, BufferedOutputStream, BufferedReader, BufferedWriter은 내부적으로 버퍼를 사용하여 데이터를 모아서 한 번에 처리한다. 이는 입출력 효율을 대폭 향상시킨다.

■ 데이터 변환 : DataInputStream, DataOutputStream은 기본 데이터 타입(int, float, long 등)을 쉽게 읽고 쓸 수 있게 해준다.

■ 객체 직렬화 : ObjectInputStream, ObjectOutputStream은 객체를 바이트 스트림으로 직렬화하거나 바이트 스트림에서 객체를 복원하는 기능을 제공한다. 

■ 문자 인코딩 : InputStreamReader, OutputStreamWriter은 바이트 스트림을 문자 스트림으로 변환하거나 반대 작업을 할 때 사용되며, 인코딩 변환을 처리한다. 

 

FileInputStream fis = new FileInputStream("input.txt"); // 기반 스트림
BufferedInputStream bis = new BufferedInputStream(fis); // 보조 스트림