I/O
컴퓨터의 5대 기능인 입력/출력/연산/저장/제어 중 입력(Input)과 출력(Ouput)을 줄여 I/O라고 말한다.
스트림 / 버퍼 / 채널 기반의 I/O
◾ 스트림
입출력을 도와주는 모듈로써 Stream이라는 단어 그대로 흐름
을 의미하며, 한 방향으로만 진행하는 단방향통신
이다.
◾ 버퍼
일종의 데이터 공간으로 메모리간, 컴퓨터와 사용자간의 속도차이로 생기는 병목현상
을 줄이기 위한 공간
데이터를 쌓아두고 한번에 찾을 수 있는 데이터 공간으로 이를 이용하여 데이터를 입출력하는 방식으로 속도향상을 꾀할 수 있다.
◾ 채널
스트림과 다르게 양방향 통신
이 가능한 방법으로 non-blocking/비동기 방식 모두 지원한다.
버퍼를 통해 데이터를 읽고 쓴다.
Selector
socket과 채널이 연결되어 클라이언트 연결시 스레드를 생성하는 대신 하나의 selector에 여러개의 채널을 생성하여 다수의 클라이언트를 대응할 수 있다.
InputStream과 OutputStream
Stream기반의 클래스의 최상위 클래스(추상 클래스) 이다.
바이트 스트림들이 갖는 메서드가 다음과 같이 존재한다.
◾ InputStream
메서드
메서드 | 기능 |
---|---|
read() | 입력 스트림으로부터 1바이트를 읽고 읽은 바이트를 리턴한다. |
read(byte[] b) | 입력 스트림으로부터 읽은 바이트들을 매개값으로 주어진 바이트 배열b에 저장하고 실제로 읽은 바이트 수를 리턴한다. |
read(byte[] b, int off, int len) | 입력 스트림으로부터 len개의 바이트만큼 읽고 매개값으로 주어진 바이트 배열 b[off]부터 len개까지 저장한다. 그리고 실제로 읽은 바이트 수인 len개를 리턴한다. 만약 len개를 모두 읽지 못하면 실제로 읽은 바이트 수를 리턴한다. |
close() | 사용한 시스템 자원을 반납하고 입력스트림을 닫는다. |
◾ OutputStream
메서드
메서드 | 기능 |
---|---|
write(int b) | 출력 스트림으로 부터 1바이트를 보낸다. |
write(byte[ ] b) | 출력 스트림으로부터 주어진 바이트 배열 b의 모든 바이트를 보낸다. |
write(byte[ ] b, int off, int len) | 출력 스트림으로 주어진 바이트 배열 b[off]부터 len개까지의 바이트를 보낸다. |
flush() | 버퍼에 잔류하는 모든 바이트를 출력한다. |
close() | 사용한 시스템 자원을 반납하고 출력 스트림을 닫는다. |
Byte와 Character 스트림
◾ Byte Stream
데이터를 Byte단위로 전송하며 바이트로 구성된 파일인 오디오,이미지,동영상 등을 읽고 쓰는데 적합한 스트림이다.
Input 종류
클래스 | 기능 |
---|---|
AudioInputStream | 오디오 포맷에 특화된 프레임 단위 스트림 입력 |
ByteArrayInputStream | 바이트 배열을 바이트 스트림으로 변환 입력 |
BufferedInputStream | 버퍼를 이용한 바이트 스트림 입력 |
FileInputStream | 파일을 바이트 단위로 읽어들여 바이트 스트림 입력 |
FilterInputStream | 버퍼와 같은 필터에 의한 바이트 스트림 입력 |
InputStream | 바이트 스트림의 입력을 위한 추상 클래스 |
ObjectInputStream | 자바 객체를 직렬화 시켜 읽어들여 스트림으로 변환 |
PipedInputStream | 바이트 스트림을 읽어들여 연결된 PipedOutputStream으로 동시에 전달 |
SequenceInputStream | 서로 다른 InpustStream을 순차적으로 입력하기 위한 클래스 |
StringBufferInputStream | 문자열 스트림 입력을 위한 클래스, JDK 1.1 이후 StreamReader 클래스로 대체되었다. |
Output 종류
클래스 | 기능 |
---|---|
ByteArrayOutputStream | 바이트 스트림을 바이트 배열로 출력 |
FileOutputStream | 바이트 스트림을 바이트 파일로 출력 |
FilterOutputStream | 버퍼와 같은 필터가 추가된 바이트 스트림 출력을 위한 추상 클래스 |
ObjectOutputStream | 바이트 스트림을 직렬화된 객체 형식으로 출력 |
OutputStream | 바이트 출력 스트림을 위한 추상 클래스 |
PipedOutputStream | PipedInputStream의 입력 스트림을 출력 |
void byteArrayTest(){
byte[] input = {0,1,2,3,4,5,6,7,8,9};
byte[] output = null;
byte[] temp = new byte[4];
ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
int len;
try {
while ((len = inputStream.read(temp)) != -1){
outputStream.write(temp,0,len); //read함수를 통해 읽은 데이터를 outputStream에 write
}
output = outputStream.toByteArray(); //OutputStream의 데이터를 output배열에 저장
System.out.println("output : "+ Arrays.toString(output));
inputStream.close();
outputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
inputStream의 read함수가 더이상 데이터를 읽지 못하면 -1을 리턴하는 것을 이용해서 while문을 통해 inputStream을 끝까지 읽을 수 있으며, ByteArray~
스트림을 이용하여 4바이트 단위로 데이터를 읽고 쓸 수도 있다.
◾ Char Stream
자바는 char형도 2바이트이기 때문에 바이트단위 스트림은 문자열(text)를 전송하는데 적합하지 않을 수 있기 때문에 문자 기반의 스트림을 제공한다.
클래스명도 뒤에 Stream이 아닌 Reader
를 붙여 사용한다.
Input 종류 (Reader)
클래스 | 기능 |
---|---|
BufferedReader | 버퍼를 이용한 문자 스트림 입력 |
CharArrayReader | 문자 배열의 입력 |
FileReader | 파일을 문자 스트림으로 변환해 입력 |
FilterReader | 버퍼와 같은 필터에 의한 문자 스트림 입력 |
InputStreamReader | 바이트 스트림을 문자 스트림으로 변환 |
LineNumberReader | 버퍼를 이용한 문자 스트림 입력, 라인번호 저장 |
PipedReader | 문자 스트림을 읽어들여 연결된 PipedWriter로 동시에 전달 |
Reader | 바이트 입력 스트림을 문자 스트림으로 변환하기 위한 추상 클래스 |
StringReader | 문자열 데이터를 문자 스트림으로 입력 |
Output 종류 (Writer)
클래스 | 기능 |
---|---|
BufferedWriter | 문자 스트림을 버퍼를 이용해 문자열 단위로 출력 |
CharArrayWriter | 문자 스트림을 문자 배열 단위로 출력 |
FilterWriter | 버퍼와 같은 필터가 추가된 문자 스트림 출력을 위한 추상 클래스 |
OutputStreamWriter | 문자 스트림을 바이트 스트림으로 변환 출력 |
PipedWriter | PipedReader에서 전달받은 문자 스트림을 바로 출력 |
PrintWriter | 형식이 있는 Writer 객체를 문자 스트림으로 출력 |
StringWriter | 문자 스트림을 문자열 데이터로 출력 |
표준 스트림
표준 입출력 장치(콘솔)에 입출력을 위해 자바에서 제공하는 스트림이다.
java.lang
패키지에 System
클래스를 통해 제공한다.
변수 | 입출력 | 기능 |
---|---|---|
System.in | Input | 콘솔의 데이터를 입력받음 |
System.out | Output | 콘솔에 데이터를 출력 |
System.err | Output | 콘솔에 에러를 출력 |
◾ 키보드문자 입력 방법
Scanner 이용
Scanner scanner = new Scanner(System.in);
Scanner 클래스를 통해 표준입출력장치로 부터 데이터를 받아올 수 있으며 next()
, nextLine()
, nextByte()
, nextDouble
…과 같은 메서드를 통해 읽어 올 수 있다.
System.in.read() 이용
System클래스에서 제공하는 read함수를 이용하여 입력을 받을 수 있으며, 1byte씩만 데이터를 읽어 ascii코드에 해당하는 int형을 리턴하는 함수이다.
BufferedReader 이용
한문자씩 스트림에서 읽어오는 InputStreamReader
를 버퍼를 사용해서 문자열 처리를 편하게 해주는 방법이 있다.
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String str = bufferedReader.readLine();
bufferedReader.close();
Scanner처럼 데이터 타입에 맞게 파싱해주는 메소드를 따로 제공하지 않기 때문에, Intger.parseInt()
와 같은 메서드를 이용해서 형변환을 해주어야 하고 Scanner보다는 빠른 입력처리가 가능하다.
◾ 데이터 출력 방법
모두가 잘 아는 System.out.println()
, System.out.print()
등과 같이 System의 메서드를 이용하는 방법이 있는데, 이를 남발하면 시스템 성능 저하의 원인이 될 수 있다.
다른 방법으로는 BufferReader
와 같은 방법으로 BufferedWriter
를 이용한 방법이 있다.
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
bufferedWriter.write("Hello\n");
bufferedWriter.close();
println()
처럼 자동으로 줄바꿈을 해주는 메서드는 따로 존재하지 않기 때문에 개행문자 (\n
)를 직접 입력해주어야 하고, IOException
의 예외처리를 해주어야 한다.
파일 읽고 쓰기
◾ 바이트 기반 스트림 (FileStream)
FileInputStream, FileOutputStream을 이용한 방법
void FileOutputStreamTest(){
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
try{
fileOutputStream = new FileOutputStream("src/test/IoTest/outputFile");
for(char ch='a'; ch <='z'; ch++){
fileOutputStream.write(ch);
}
File file = new File("src/test/IoTest/outputFile");
fileInputStream = new FileInputStream(file);
int c;
while((c = fileInputStream.read()) != -1){
System.out.print((char) c);
}
fileInputStream.close();
fileOutputStream.close();
}catch (FileNotFoundException e){
System.out.println("Not Found file");
}catch (IOException e){
System.out.println("IO Exception");
}
}
◾ 문자기반 스트림
FileReader와 FileWriter를 이용하여 사용하는 방법
void fileWriterTest(){
FileWriter fileWriter =null;
FileReader fileReader = null;
try{
fileWriter = new FileWriter("src/test/IoTest/fileWriter.txt");
for(int i=0; i < 10; i++){
fileWriter.write(i);
}
fileWriter.close();
fileReader = new FileReader("src/test/IoTest/fileWriter.txt");
int c;
while((c = fileReader.read()) != -1){
System.out.print(c);
}
fileReader.close();
}catch (IOException e){
e.printStackTrace();
}
}
◾ 차이점
FileInputStream/FileOutputStream
은 InputStream/OutputStream
을 상속받고 있고 FileReader/FileWriter
은 Reader/Writer
를 상속받고 있다.
때문에, FileReader/Writer은 바이트를 문자로 변환하여 입출력을 처리하고 FileInput/OutputStream은 1바이트 이상인 한글
등을 처리하기 위해서 버퍼를 사용해서 처리해야 한다.
파일 포인터의 시작 포인트가 다른데 byte
방식은 outputStream을 close안해도 input에서 읽을 수 있고
char
방식은 close를 안하면 reader로 읽을 수가 없다.
void FileOutputStreamTest(){
FileOutputStream fileOutputStream = null;
FileInputStream fileInputStream = null;
try{
fileOutputStream = new FileOutputStream("src/test/IoTest/outputFile.txt");
for(int i=0; i< 10; i++){
fileOutputStream.write(i);
}
fileInputStream = new FileInputStream("src/test/IoTest/outputFile.txt");
int c;
while((c = fileInputStream.read()) != -1){
System.out.print(c);
}
fileInputStream.close();
fileOutputStream.close();
}catch (FileNotFoundException e){
System.out.println("Not Found file");
}catch (IOException e){
System.out.println("IO Exception");
}
}
byte방식은 FileOutputStream으로 쓰고나서 close하지 않은 파일을 FileInputStream으로 처음부터 읽는게 가능하다.
void fileWriterTest(){
FileWriter fileWriter =null;
FileReader fileReader = null;
try{
fileWriter = new FileWriter("src/test/IoTest/fileWriter.txt");
for(int i=0; i < 10; i++){
fileWriter.write(i);
}
fileReader = new FileReader("src/test/IoTest/fileWriter.txt");
int c;
while((c = fileReader.read()) != -1){
System.out.print(c);
}
fileWriter.close();
fileReader.close();
}catch (IOException e){
e.printStackTrace();
}
}
char방식인 FileReader은 FileWriter로 작성후에 close하지 않은 파일을 읽으려고 하면 파일포인터가 끝을 가리키기 때문에 읽히지 않는다.
인코딩
byte단위로 입출력하는 FileInputStream/FileOutputStream에서 아스키코드가 아닌 그 외 문자들을 제대로 출력하기 위한 인코딩을 설정해주어야한다.
Reader의 클래스중 하나인 Input/OutputStreamReader를 이용할 수 있다.
◾ InputStreamReader / OutputStreamReader
파일의 인코딩 방식을 지정할 수 있고, 바이트입력 스트림
에 연결되어 문자입력스트림
인 Reader로 변환시킨다.
void encodingTest(){
try {
FileOutputStream fileOutputStream = new FileOutputStream("src/test/IoTest/encoding.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8");
outputStreamWriter.write("안녕하세요");
outputStreamWriter.close();
fileOutputStream.close();
FileInputStream fileInputStream = new FileInputStream("src/test/IoTest/encoding.txt");
InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf-8");
int c;
while((c = inputStreamReader.read()) != -1){
System.out.print((char) c);
}
inputStreamReader.close();
fileInputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}