|
입출력 Stream
: Stream 이란 자료의 입출력을 도와주는 중간 매개체다.
파일 저장용 디스크, 키보드, 모니터, 마우스, 메모리, 네트웍 등 입출력과 관련된 장치들을 통해 자료를 처리함에 있어 각각의 하드웨어 장치에 대한 입출력 원리를 알지 못해도 자바에서 제공하는 Stream을 이용하면 간단히 원하는 작업을 할 수 있다.
즉, 이러한 입출력 자료를 처리하는 공통된 방법이 자바에서는 Stream 이라는 것이다. 이러한 Stream은 FIFO 구조로서 단방향이며, 대개 read() 계열과 write() 계열로 나뉘어 진다. (예: System.in.read(), System.out.print()…)
* 스트림: 흐르는 데이터 혹은 파이프의 구조를 개념으로 한다.
스트림은 장치A에서 장치B로 이동하는 데이터의 흐름이라고 할 수 있다.
특히 프로그래머 입장에서 에서 스트림은 아래와 같은 것들을 말한다.
1) 키보드 입력 : 사용자가 키보드로 입력하는 문자, 숫자, 특수문자 등
2) 파일 데이터 : 파일은 시작과 끝이 있는 데이터
3) HTTP기반 웹 송수신 데이터 : 브라우저가 요청하고 서버가 응답하는 HTTP 형태의 데이터
* 입출력 스트림: 데이터를 읽고 쓰는 구조를 프로그램의 구조로 만들어 놓은 것
* 버퍼 : 데이터를 임시적으로 담아두는 공간
1. 자바 입출력 스트림(stream) 개념
- 단 방향이다.
- 버퍼를 가질 수 있다.
- FIFO(Frist In First Out)의 구조를 사용한다.
- 네트워크에서 지연상태에 빠질 수 있다.
- 데이터를 조작하기 위해서는 버퍼를 사용하여야 한다.
2. 입력 스트림 클래스(InputStream Class) : 데이터를 프로그램 안으로 읽어 들이는 스트림
3. 출력 스트림 클래스(OutputStream Class) : 데이터를 프로그램 밖으로 출력하는 스트림
----------------------------------------------------------------
- Summary -
1. 자바의 입출력(I/O)은 입출력 스트림(stream)의 구조에 의해 객체화 되어 있다.
- 스트림은 데이터의 흐름 구조를 의미한다. ( 통로, 관 )
- 스트림은 FIFO(First In First Out) 구조를 가지고 있따.
- 스트림은 버퍼의 기능을 가질 수 있으며, 조작되어 질 수 있다.
- 스트림은 입력과 출력에 각각 단 방향성이다.
- 스트림은 메모리, 파일, 네트워크 등의 데이터를 주고 받아야하는 모든 객체들로 된다.
2. 자바 입출력 스트림의 구분은 버퍼(buffer)의 기능과 입출력하는 단위로 구분되어 진다.
- 입출력 단위가 바이트 (byte) 단위인 경우
1) 버퍼를 사용하지 않는 경우
2) 버퍼를 사용하는 경우
- 입출력 단위가 문자(char) 단위인 경우
1) 버퍼를 사용하지 않는 경우
2) 버퍼를 사용하는 경우
3. 자바 입력 스트림은 InputStream과 Reader 클래스의 형태로 지원한다.
바이트 단위 | 문자 단위 | 설명
InputStream Reader 기본 입력 스트림 클래스
BufferedInputStream BufferedReader 버퍼를 가진 입력 스트림 클래스
FileInputStream FileReader 파일 입력 스트림 클래스
FilterInputStream FilteredReader 필터 지원 입력 스트림 클래스
DataInputStream 없음 기본형 데이터를 다루는 스트림 클래스
4. 자바 출력 스트림은 OutputStream과 Writer 클래스의 형태로 지원한다.
바이트 단위 | 문자 단위 | 설명
OutputStream Writer 기본 출력 스트림 클래스
BufferedOutputStream BufferedWriter 버퍼를 가진 출력 스트림 클래스
FileOutputStream FileWriter 파일 출력 스트림 클래스
FilterOutputStream FilteredWriter 필터 지원 출력 스트림 클래스
PrintStream PrintWriter 출력을 위한 다양한 메소드 지원
DataInputStream 없음 기본형 데이터를 출력하는 스트림 클래스
참고 :
FileInputStream : 파일로부터 바이트 단위로 자료를 읽을 때 사용하는 바이트 기반 입력스트림.
그림, 오디오, 비디오, 텍스트 파일 등 파일로 작성된 모든 자료를 읽을 수 있다.
InputStreamReader : 보조 스트림이다. 바이트 입력 스트림에 연결되어 문자 스트림인 Reader로 변환시키는 등의 역할을 한다.
BufferedReader : 문자입력 스트림에 연결되어 버퍼를 제공해 주는 보조 스트림이다.
BufferedInputStream : 바이트입력 스트림에 연결되어 버퍼를 제공해 주는 보조 스트림이다.
FileOutputStream : 바이트 단위로 자료를 저장할 때 사용하는 바이트 기반 출력 스트림이다.
OutputStreamWriter : 바이트 출력 스트림에 연결되어 문자출력 스트림인 Writer로 변환시키는 등의 역할을 한다.
BufferedWriter : 문자출력 스트림에 연결되어 버퍼를 제공해 주는 보조 스트림이다.
BufferedOutputStream : 바이트출력 스트림에 연결되어 버퍼를 제공해 주는 보조 스트림이다.
ObjectInputStream / ObjectOutputStream : 객체를 직렬화 한다.
직렬화는 객체 데이터를 일렬로 늘어선 연속적인 바이트로 구성한 자료를 말한다.
예제)
파일 처리 방법을 크게 나누자면 2가지 인데 바이트 단위의 파일을 처리 하고자 할 때 FileInputStream, FileOutputStream을 사용하고, 문자 단위의 파일을 처리하고자 할 때 FileReader, FileWriter 을 사용한다. 즉, 1바이트씩 처리해야 하는 경우에는 Stream이 붙은 클래스를 사용하고 한글이나 문자 관련 처리를 원할 때는 UTF-8 로 처리를 해야하기 때문에 Reader, Writer가 붙은 클래스를 사용하는 것이 좋다. 그래야 추가적인 처리를 하지 않아도 한글이 깨지지 않는다.
1. 입력과 출력 스트림을 제어하는 FileInputStream, FileOutputStream 클래스
파일을 읽고 쓰는 클래스이며 읽을 때 파일이 존재하지 않으면 FileNotFoundException 예외를 발생시킨다. 파일을 쓸 경우에 경로가 일치하지 않으면 예외를 발생시키지 않고 파일을 생성하게 된다. 주석이 달려 있지만 간단하게 함수 별로 설명하자면 3가지는 읽기에 대한 방법들을 보여주는 것이고 나머지 2가지는 파일에 데이터를 쓸 때 옵션에 따라 이어 쓸 것인지 덮어 쓸 것인지에 대한 내용이다.
FileInputStream 을 이용해서 파일의 내용을 읽는다 한 바이트씩 읽는 것이다
public static void FileStreamSample1() throws Exception {
File f = new File("C:\\data.txt");
if (f.exists()) {
FileInputStream fis = new FileInputStream("a.txt");
int i;
while ((i = fis.read()) != -1) {
System.out.print((char) i);
}
fis.close();
} else {
System.out.println("해당 파일이 없습니다.");
}
}
바이트 배열을 미리 준비해둔 상태에서 데이터를 읽고 준비한 바이트 배열에 모두 채운다. 이렇게 하는 이유는 한글은 유니코드이기 때문에 2 바이트가 필요하다. 위 처럼 1 바이트만 읽어서 출력하면 한글일 경우 깨진다. 이를 방지하기 위해 예제 처럼 10 바이트를 채운 후에 사용하려는 것이다.
public static void FileStreamSample2() throws Exception {
FileInputStream fis = new FileInputStream("G:\\data.txt");
int count;
byte[] b = new byte[10];
while ((count = fis.read(b)) != -1) {
for (int i = 0; i < count; i++) {
System.out.print((char) b[i]);
}
}
fis.close();
}
읽을 내용만큼 바이트 배열을 미리 만든다. 파일의 내용을 읽어서 바이트 크기만큼 채우게 되면 프로그램이 끝나게 된다. 바이트 배열의 크기를 만들 때 짝수배 값을 넣게 되면 한글이 깨질 일이 없다.
public static void FileStreamSample3() throws Exception {
String url = "C:\\data.txt";
File f = new File(url);
// int fileSize = (int) f.length();
int fileSize = 200;
System.out.println("파일 크기:" + fileSize);
//파일 사이즈에 해당하는 배열 만들기
byte[] b = new byte[fileSize];
//스트림을 이용해서 배열에 데이터 채우기
FileInputStream fis = new FileInputStream(url);
int pos = 0;
int size = 10;
int temp;
while ((size = fis.read(b, pos, size)) > 0) {
pos += size;
temp = b.length - pos;
if (temp < 10) {
size = temp;
}
}
fis.close();
System.out.println("읽은 바이트 수:" + pos);
//배열을 통째로 파일에 기록하기.
FileOutputStream fos = new FileOutputStream("C:\\test.txt");
fos.write(b);
fos.close();
}
파일 쓰기는 생성자 인자 값을 안넘기므로 FileOutputStream 으로 파일 생성할 때 없으면 새로 만들고 있으면 덮어쓰게 된다.
public static void FileStreamSample4() throws Exception {
FileOutputStream stream = new FileOutputStream("C:\\data.txt");
stream.write(101);
stream.write(102);
stream.write(103);
stream.close(); // 스트림 닫기
}
위 소스와 달리 파일 생성자 두번째 인자로 true 를 넘긴다. 그러면 파일을 쓸 때 기존에 값이 있으면 이어쓰기가 되는 것이다.
public static void FileStreamSample5() throws Exception {
FileOutputStream stream = new FileOutputStream("C:\\data.txt", true);
stream.write(65);
stream.write(66);
stream.write(67);
stream.close();
}
2. 문자 단위의 입력, 출력을 제어하는 FileReader, FileWriter 클래스
문자 스트림으로 한 문자씩 읽기 때문에 한글이 깨지는 현상은 없다. 파일을 읽을 때 존재하지 않으면 FileNotFoundException 예외를 발생시키며 파일을 쓸 때 경로가 존재하지 않으면 IOException 이 발생한다.
import java.io.*;
public class FileReaderWriter {
public static void main(String[] args) {
try {
// 파일 쓰기
BufferedWriter writer = new BufferedWriter(new FileWriter("C:\\test.txt"));
writer.write("파일쓰기 테스트\r\n");
writer.write("다음라인");
writer.newLine(); // \r\n 써도 되고 newLine 함수를 사용해도 된다
writer.write("newLine 사용해서 내림\r\n");
writer.close();
// 파일 읽기
BufferedReader reader = new BufferedReader(new FileReader("C:\\test.txt"));
String data = "";
// readLine 사용해 한 라인씩 읽어들인다
while ((data = reader.readLine()) != null) {
System.out.println(data);
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3. 파일의 입출력을 중간에 버퍼를 둬서 성능을 향샹시킨 방법
클래스는 BufferedInputStream, BufferedOutputStream 이다. 1번과 2번처럼 파일을 읽고 쓰게 되면 매번 자원을 엑세스하기 때문에 상당한 부하가 있을 것이다. 그래서 read 함수를 호출하기 전에 버퍼의 크기 만큼 데이터를 채워진 상태에서 읽게 되면 그만큼 효율적이다.
아래 예제는 용량이 큰 이미지 파일을 스트림으로 읽어들여 성능을 비교한 내용이다.
import java.io.*;
public class CompareBufferOrNotBuffer {
public static void main(String args[]) {
File source = new File("C:/test1.jpg");
File target1 = new File("C:/test2.jpg");
File target2 = new File("C:/test3.jpg");
// 버퍼링이 지원되지 않는 스트림
FileInputStream fis = null;
FileOutputStream fos = null;
// 버퍼링이 지원되는 스트림
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
fis = new FileInputStream(source);
fos = new FileOutputStream(target1);
long startTime = System.currentTimeMillis();
while (true) {
int x = fis.read();
if (x == -1) break;
fos.write(x);
}
long endTime = System.currentTimeMillis() - startTime;
System.out.println("일반 스트림: " + endTime);
fis.close(); fos.close();
// 버퍼링 지원 스트림
bis = new BufferedInputStream(new FileInputStream(source));
bos = new BufferedOutputStream(new FileOutputStream(target2));
startTime = System.currentTimeMillis();
while (true) {
int x = bis.read();
if (x == -1) break;
bos.write(x);
}
endTime = System.currentTimeMillis() - startTime;
System.out.println("버퍼링 스트림: " + endTime);
bis.close(); bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
결과를 보면 많은 차이가 나는 것을 알 수 있다.
사운드 단음 처리
import java.io.File;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;import javax.sound.sampled.AudioSystem;import javax.sound.sampled.Clip;import javax.sound.sampled.DataLine;
public class MediaTest {
public void sori() {
File bgm;
AudioInputStream stream; // 오디오의 형식 및 길이가 지정된 입력 스트림
AudioFormat format; // sound stream 내에서 데이터의 특정 배열을 지정
DataLine.Info info; // 오디오 데이터를 시작, 전송하는 등의 제어 방법을 갖고 있다.
Clip clip; // 음성 대신에 실시간으로 스트리밍 된다. 즉, 사운드 재생 전에 로드할 수 있는 특수한 종류의 데이터 라인을 나타냄.
bgm = new File("c:/work/pack/gun.wav");
try {
stream = AudioSystem.getAudioInputStream(bgm);
format = stream.getFormat();
info = new DataLine.Info(Clip.class, format);
clip = (Clip)AudioSystem.getLine(info); clip.open(stream);
clip.start();
} catch (Exception e) {
// TODO: handle exception }
}
public static void main(String[] args) {
MediaTest mediaTest = new MediaTest();
while(true) {
try {
mediaTest.sori();
Thread.sleep(2000);
} catch (Exception e) {
// TODO: handle exception }
}
}
}