<데이터 입출력>
I/O 스트림(I/O Stream)
: 데이터는 키보드를 통해 입력하거나 파일 또는 프로그램에서 입력할 수 있습니다.
대조적으로 데이터는 모니터를 통해 출력될 수 있으며 파일에 저장하거나 다른 프로그램으로 전송할 수 있습니다.
이것을 총칭하여 데이터 입출력이라고합니다.
※ Stream API 와 I/O Stream 은 다른 것임을 인식해야 한다
△Java는 입력 스트림과 출력 스트림을 통해 데이터를 입출력합니다.
△ 스트림이란 한 방향으로 들어가 데이터가 흐르는 것을 말하며, 위 그림과 같이 데이터는 출발지에서 나온다.
도착지로 유입
– 프로그램 기준으로 데이터가 들어가면 입력 스트림(Input Stream), 데이터가 나오면 출력 스트림(OutputStream)
– 프로그램이 다른 프로그램과 데이터를 교환하려면 둘 다 입력 및 출력 스트림이 필요합니다.
– 어떤 데이터를 입출력할지에 따라 스트림은 다음의 2종류로 나눌 수 있다
● 바이트 스트림: 이미지, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용
● 문자 스트림: 문자만 입출력할 때 사용
– Java는 java.io 패키지로 데이터 입출력에 대한 라이브러리를 제공합니다.
▼ java.io 패키지는 바이트 스트림과 문자 스트림을 아래 표와 같이 이름으로 단락지어 제공한다
– 바이트 입출력 스트림의 최상위 클래스는 InputStream 와 OutputStream 이다
– 이 클래스를 상속하는 아이 클래스에는 접미사로 InputStream 또는 OutputStream 가 붙는다
(ex. 이미지와 같은 바이너리 파일의 입출력 스트림 클래스는 FileInputStream 와 FileOutputStream 이다)
– 문자 입출력 스트림의 최상위 클래스는 Reader와 Writer
– 이 클래스를 상속하는 서브 클래스에는 접미사로 Reader 또는 Writer 가 붙는다
(ex. 텍스트 파일의 입출력 스트림 클래스는 FileReader와 FileWriter입니다)
// Input Output Stream
//데이터 형태 : 바이트 스트림, 문자 스트림
//방향 : 인풋 스트림, 아웃풋 스트림
//1)바이트 단위 인풋 스트림 : InputStream
//2)바이트 단위 아웃풋 스트림 : OutputStream
//3)문자 단위 인풋 스트림 : Reader
//4)문자 단위 아웃풋 스트림 : Writer
바이트 출력 스트림 (OutputStream)
– OutputStream 는 바이트 출력 스트림의 최상위 클래스로 추상 클래스입니다.
– 모든 바이트 스트림 클래스는, 이 OutputStream 클래스를 상속해 작성됩니다.
– OutputStream 클래스에는, 모든 바이트 출력 스트림이 디폴트로 가지지 않으면 안되는 메소드가 정의되고 있습니다.
▼ OutputStream 클래스의 주요 메서드
1바이트 출력_ Write(int b) 메서드
– write(int b) 메소드는 파라미터 int(4bytes) 로 종료 1byte 만을 출력한다
– 파라미터가 int형이므로 4bytes 모드를 송신하는 것은 아니다
(예)
public static void main(String() args) {
try {
//데이터 도착지를 test1.db 파일로 하는 바이트 출력 스티림 생성
OutputStream os = new FileOutputStream("output/test1.db");
byte a = 10;
byte b = 20;
byte c = 30;
//1byte 씩 출력
os.write(a);
os.write(b);
os.write(c);
//내부 버퍼에 잔류하는 바이트를 출력하고 버퍼를 비움
os.flush();
//출력 스트림을 닫아 사용한 메모리를 해제
os.close();
} catch (IOException e) {
e.printStackTrace();
}
}
△FileOutputStream constructor 은, 주어진 파일을 생성할 수 없는 경우에 IOException 를 발생시킨다
△ write(), flush(), close() 메소드도 IOException 가 발생할 가능성이 있으므로, 예외 처리를 실시해야 한다 (try-catch)
△ OutputStream 는 내부에 작은 버퍼 (buffer) 를 가지고 있어 write() 메소드가 불려 가면 버퍼에 바이트를 우선 포함해,
버퍼가 가득 차면 순서대로 바이트를 출력합니다.
△ flush() 메소드는 내부 버퍼에 남아 있는 모든 바이트를 출력해 버퍼를 비우는 역할을 한다
△내부 버퍼를 사용하는 이유는 출력 성능을 향상시키기 위한 것이다.
△ 출력 스트림을 사용하지 않게 되었을 때는, close() 메소드를 호출해 출력 스트림이 사용하고 있던 메모리를 해방
바이트 배열 출력_ Write(byte( ) b) 메서드
: 통상 1 바이트를 출력하는 것은 드물고, 통상 바이트 배열을 통째로 출력하는 경우가 많다
write(byte( ) b) 메소드는, 파라미터로서 주어진 배열의 모든 바이트를 출력합니다.
– 배열의 일부를 출력하고 싶은 경우 write(byte( ) b, int off, int len) 방법을 사용할 수 있습니다.
– 이 메소드는 b(off) 로부터 len 개의 바이트를 출력한다
public static void main(String() args) {
try (var os = new FileOutputStream("output/output7.txt")) {
//한 바이트씩 쓰기
os.write(30);
os.write(16563);
// 여러 바이트 쓰기
byte() data = {3, 3, 3, 3, 3, 3, 3, 3, 3};
os.write(data);
//여러바이트 쓰기 (배열의 일부분)
os.write(data, 0, 3); //3bytes
os.write(data, 4, 5); //5bytes
os.write(data, 0, data.length); // 9bytes
} catch (IOException e) {
e.printStackTrace();
}
}
바이트 입력 스트림(InputStream)
– InputStream 는 바이트 입력 스트림의 최상위 클래스로, 추상 클래스입니다.
– 모든 바이트 입력 스트림은 InputStream 클래스를 상속하여 작성됩니다.
– InputStream 클래스에는, 바이트 입력 스트림이 디폴트로 가지지 않으면 안되는 메소드가 정의되고 있습니다.
▼ InputStream 클래스의 주요 메서드
1바이트 읽기_ read()
– read() 메소드는 입력 스트림로부터 1byte 를 읽어들여, int(4byte) 타입으로 돌려준다
– 따라서 반환된 4바이트 중 1바이트에만 데이터가 포함됩니다.
(ex. 입력 스트림로부터 5 개의 바이트가 들어 있으면, 아래의 이미지와 같이 read() 메소드로 1byte 씩 5 회 읽어낼 수 있다)
– 더 이상 입력 스트림로부터 바이트를 읽을 수 없는 경우는, read() 메소드는 -1 을 돌려줍니다.
읽을 수 있는 마지막 바이트까지 반복 1바이트씩 읽을 수 있다
InputStream is = ...;
while (true) {
int data = is.read(); //1 바이트를 읽고 리턴
if (data == -1) break; //-1을 리턴했을 경우 while 문 종료
}
– FileInputStream 생성자는, 주어진 파일이 존재하지 않는 경우에 FileNotFoundException를 발생시킨다
– 그리고 read(), close() 메서드에서 IOException이 발생할 수 있으므로 두 예외를 모두 처리해야합니다.
바이트 배열로 읽는다 _ read(byte( ) b)
– read(byte( ) b) 메소드는 입력 스트림로부터 주어진 배열의 길이만큼 바이트를 읽어 배열에 포함합니다.
읽은 바이트 수를 반환합니다.
– read(byte( ) b) 도 입력 스트림로부터 바이트를 읽을 수 없게 되면 -1 을 돌려줍니다.
이것을 이용하면, 읽을 수 있는 마지막 바이트까지 반복 읽을 수 있다
InputStream is = ...;
byte() data = new byte(100);
while (true) {
int num = is.read(data); //최대 100byte를 읽고, 읽은바이트는 배열 data 저장, 읽은 수는 리턴
if (num == -1) break; //-1을 리턴하면 while 문 종료
}
– 많은 양의 바이트를 읽는 read(byte()b) 메소드를 사용하는 것이 좋습니다.
– 입력 스트림로부터 100개의 바이트가 들어온다면, read() 메소드는 100회를 반복해 읽어야 하지만,
read(byte( ) b) 메서드는 한번 읽으면 배열 길이만 읽으므로 읽기 횟수가 크게 줄어듭니다.
(파일 복사 예)
ㄴ 파일 복사의 원리는 FileInputStream에서 읽은 바이트를 즉시 FileOutStream에 출력합니다.
public static void main(String() args) throws Exception{
String originalFileName = "output/bread.jpg";
String targetFileName = "output/bread_copy3.jpg";
//입출력 스트림 생성
InputStream is = new FileInputStream(originalFileName);
OutputStream os = new FileOutputStream(targetFileName);
//읽은 바이트를 저장할 배열 생성
byte() data = new byte(1024);
while(true) {
//최대 1024바이트를 읽고 배열에 저장, 읽은 바이트는 리턴
int num = is.read(data);
//파일을 다 읽으면 while문 종료
if(num == 1) break;
//읽은 바이트 수만큼 출력
os.write(data, 0, num);
}
//내부 버퍼 잔류 바이트를 출력하고 버퍼를 비움
os.flush();
os.close();
is.close();
System.out.println("복사가 잘 되었습니다.
");
}
– Java 9에서보다 편리하게 입력 스트림에서 출력 스트림으로 바이트 복사 transferTo() 방법
InputStream에 추가됨
▼아래의 코드를 1행으로 옮겨놓는 것이 가능
byte() data = new byte(1024);
while(true) {
int num = is.read(data);
if(num == 1) break;
os.write(data, 0, num);
}
↓
is.transferTo(os);
문자 입출력 스트림
– 바이트 입출력 스트림인 InputStream 및 OutputStream에 대응하는 문자 입출력 스트림
리더그리고 Writer예
– 입출력되는 단위가 문자인 것을 제외하고, 바이트 입출력 스트림과 사용 방법은 동일
문자 출력_Writer
– Writer는 문자 출력 스트림의 최상위 클래스이며 추상 클래스입니다.
– 모든 문자 출력 스트림 클래스는 Writer 클래스를 상속하여 작성됩니다.
– Writer 클래스에는, 모든 캐릭터 출력 스트림이 디폴트로 가지지 않으면 안되는 메소드가 정의되고 있습니다
▼ Writer 클래스의 주요 메소드
– Writer는 OutputStream과 사용 방법은 동일하지만 출력 단위가 문자(char)
– 캐릭터 라인을 출력하는 write(String str) 메소드를 추가로 제공
(하나의 문자열, 문자 배열, 문자열을 각각 출력하는 방법의 예)
public static void main(String() args) {
try {
//문자 기반 출력 스트림 생성
Writer writer = new FileWriter("output/output1.txt");
//1문자씩 출력
char a="A";
writer.write(a);
char b = 'B';
writer.write(b);
//char 배열 출력
char() arr = { 'C', 'D', 'E' };
writer.write(arr);
//문자열 출력
writer.write("FGH");
//버퍼에 잔류하고 있는 문자들을 출력하고, 버퍼를 비움
writer.flush();
//출력 스트림을 닫고 메모리 해제
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
문자 읽기_리더
– Reader는 문자 입력 스트림의 최상위 클래스이며 추상 클래스입니다.
– 모든 문자 입력 스트림 클래스는 Reader 클래스를 상속하여 생성됩니다.
– Reader 클래스에는, 문자 입력 스트림이 디폴트로 가지지 않으면 안되는 메소드가 정의되고 있습니다.
▼ Reader 클래스의 주요 메소드
– Reader는 InputStream과 사용 방법은 동일하지만 출력 단위가 문자(char)
(문자를 하나씩 읽거나 문자 배열로 읽는 방법의 예)
public static void main(String() args) {
try {
Reader reader = null;
//1 문자씩 읽기
reader = new FileReader("output/writer2.txt");
while (true) {
int data = reader.read();
if (data == -1) break;
System.out.println((char) data);
}
reader.close();
System.out.println();
//문자 배열 읽기
reader = new FileReader("output/writer2.txt");
char() data = new char(100);
while(true) {
int num = reader.read(data);
if (num == -1) break;
for (int i = 0; i < num; i++) {
System.out.println(data(i));
}
}
reader.close();
} catch (FileNotFoundException e ) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
보조 스트림
: 2차 스트림은 다른 스트림과 연동하여 다양한 유용한 기능을 제공하는 스트림입니다.
보조 스트림은 그 자체가 입출력을 실행할 수 없기 때문에, 입출력 소스로부터 직접 생성된 입출력 스트림에
연결하여 사용해야 함
– I/O 스트림에 2차 스트림을 연결하려면 2차 스트림을 작성할 때 생성자 매개변수로 I/O 스트림을 제공합니다.
▼ 예를 들어 바이트 입력 스트림인 FileInputStream 에 InputStreamReader 보조 스트림을 연결하는 코드는 이하와 같다
InputStream is = new FileInputStream("...");
InputStreamReader reder = new InputStreamReader(is);
– 2차 스트림은 다른 2차 스트림과 연관되며 스트림 체인으로 구성할 수 있습니다.
▼ 예를 들어 문자 변환 보조 스트림인 InputStreamReader 에 BufferedReader 보조 스트림을 연결하는 코드는
다음과 같이
InputStream is = new FileInputStream("...");
InputStreamReader reader = new InputStreamReader(is);
BufferedReader br = new BufferedReader(reader);
▼자주 사용되는 보조 스트림
성능 향상 스트림_ Buffered
– CPU와 메모리가 아무리 우수하더라도 하드디스크의 입출력이 느려지면 프로그램의 실행 성능은 하드디스크의 처리속도로
맞추다
– 네트워크로 데이터를 전송할 때도 느린 네트워크 환경이라면 컴퓨터 사양이 아무리 좋더라도 메신저와 게임 속도는
늦을 수밖에 없다
– 위의 문제에 대한 완전한 해결책은 불가능하지만 프로그램이 I / O 소스와 직접 작업하지 않고 중간에 메모리 버퍼 (buffer)
그리고 작업함으로써 실행 성능을 향상시킬 수 있습니다.
△출력 스트림의 경우, 직접 하드 디스크에 데이터를 보내지 않고 메모리 버퍼에 데이터를 보내는 것으로 출력 속도를
개선할 수 있는
△ 버퍼는 데이터가 축적되기를 기다린 후 가득 차면 데이터를 한 번에 하드 디스크로 보냅니다.
출력 횟수 감소
△입력 스트림에서도 버퍼를 사용하면 읽기 성능이 좋아진다
△ 하드 디스크에서 직접 읽는 것보다 메모리 버퍼에서 읽는 것이 더 빠릅니다.
△상기와 같이 메모리 버퍼를 제공하여 프로그램의 실행 성능을 향상시키는 보조 스트림이 있다.
로바이트 스트림에 BufferedInputStream, BufferedOutputStream예,
로문자 스트림에 BufferedReader, BufferedWriter예
▼ 보조 스트림을 연결하는 방법
BufferedInputStream bis = new BufferedInputStream(바이트 입력 스트림);
BufferedOutputStream bos = new BufferedOutputStream(바이트 출력 스트림);
BufferedReader br = new BufferedReader(문자 입력 스트림);
BufferedWriter bw = new BufferedWriter(문자 출력 스트림);