일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
- 예제
- node.js
- 노드
- ngrinder
- 이벤트 루프
- 파헤쳐보자
- 특징
- 소켓
- 스레드
- RDF
- socket.io
- express
- 스크럼 마스터
- java
- 지식 그래프
- scrum
- 개발자
- 노드js
- Stream
- node
- nodejs
- C++
- 자바
- Groovy
- Python
- benchmark
- 스크럼
- Django
- Knowledge Graph
- Router
- Today
- Total
라봉이의 개발 블로그
java의 정석 14강. 입출력 I/O (상) 본문
1. 자바에서의 입출력
1.1 입출력이란?
입출력이란 컴퓨터 내부 또는 외부의 장치와 프로그램간의 데이터를 주고받는 것을 말한다.
1.2 스트림
스트림이란 일종의 추상적인 개념인데 입출력 기기나 프로세스, 파일 등 데이터가 어디로 가는 지, 어디로 나왔는 지 상관없이 통일된 방식으로 데이터를 다루기 위한 가상의 개념이다.
Node.js의 stream을 알고 싶다면 다음의 링크를 타고가면 된다.
스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간어 건너뜀 없이 연속적으로 데이터를 주고받는다. 큐와 같은 FIFO 구조로 되어 있다고 생각하면 이해하기 편할 것이다.
1.3 바이트기반 스트림 - InputStream, OutputStream
스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력스트림이 있다.
입력 스트림 |
출력 스트림 |
압출력 대상의 종류 |
FileInputStream |
FileOutputStream |
파일 |
ByteArrayInputStream |
ByteArrayOutputStream |
메모리(byte 배열) |
PipedInputStream |
PipedOutputStream |
프로세스(프로세스간 통신) |
AudioInputStream |
AudioOutputStream |
오디오장치 |
어떤 작업을 할 것인지에 따라 나뉘므로 적절한 상황에 맞게 사용하면 된다.
이들 모두 InputStream, OutputStream의 자손들이며, 각각 읽고 쓰는데 필요한 추상메서드를 자신에 맞게 구현해 놓았다.
stream은 입출력을 처리할 수있는 표준화된 방법을 제공함으로써 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능하다.
InputStream |
OutputStream |
abstract int read() |
abstract void write(int b) |
int read(byte[] b) |
void write(byte[] b) |
int read(byte[] b, int off, int len) |
void write(byte[] b, int off, int len) |
위의 표는 InputStream과 OutputStream에 정의된 읽기와 쓰기를 수행하는 메서드이다.
1.4 보조스트림
위의 표1의 스트림의 기능을 보완하기 위한 보조스트림이 제공된다.
보조스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림 기능을 향상시키거나 새로운 기능을 추가할 수 있다.
예를 들어 test.txt라는 파일을 읽기위해 FileInputStream을 사용할 때, 입력 성능을 향상시키기 위해 버퍼를 사용하는 보조스트림인 BufferedInputStream을 사용하는 코드는 다음과 같다.
// 먼저 기반스트림을 생성
FileInputStream fis = new FileInputStream("test.txt");
// 기반스트림을 이용해서 보조스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read(); // 보조스트림인 bis로부터 데이터를 읽는다.
보조스트림의 종류
입력 |
출력 |
설명 |
FilterInputStream |
FilterOutputStream |
필터를 이용한 입출력 처리 |
BufferedInputStream |
BufferedOutputStream |
버퍼를 이용한 입출력 성능 향상 |
DataInputStream |
DataOutputSttream |
int, float와 같은 기본형 단위로 데이터를 처리하는 기능 |
SequenceInputStream |
SequenceOutputStream |
두 개의 스트림을 하나로 연결 |
LineNumberInputStream |
없음 |
읽어 온 데이터의 라인 번호를 카운트(jdk 1.1부터 LineNumberReader로 대체) |
ObjectInputStream |
ObjectOutputStream |
데이터를 객체단위로 읽고 쓰는데 사용. 주로 파일을 이용하며 객체 직렬화와 관련 있음 |
없음 |
PrintStream |
버퍼를 이용하며, 추가적인 print 관련 기능 |
PushbackInputStream |
없음 |
버퍼를 이용해서 읽어 온 데이터를 다시 되돌리는 기능 |
1.5 문자기반 스트림 - Reader, Writer
지금까지 알아본 스트림은 모두 바이트기반의 스트림이었다.
자바에서는 문자를 의미하는 char형이 2 byte이기 때문에 바이트기반의 스트림으로 2 byte문자를 처리하는 데는 어려움이 있다.
이 점을 보완하기 위해서 문자기반의 스트림이 제공된다. 문자데이터를 입출력할 때는 문자기반 스트림을 사용하자
InputStream -> Reader
OutputStream -> Writer
2. 바이트기반 스트림
2.1 InputStream과 OutputStream
메서드 명 |
설 명 |
int available() |
스트림으로부터 읽어 올 수 있는 데이터의 크기를 반환한다. |
void close() |
스트림을 닫음으로써 사용하고 있던 자원을 반환한다. |
void mark(int readlimit) |
현재 위치를 표시해 놓는다. 후에 reset()에 의해서 표시해 놓은 위치로 다시 돌아갈 수 있다. readlimit: 되돌아갈 수 있는 byte의 수 |
boolean markSupported() |
mark()와 reset()을 지원하는 지를 알려 준다. 이 메서드로 먼저 확인을 해야한다. |
abstract int read() |
1 byte를 읽어온다. 더 이상 읽을 데이터가 없다면 -1을 반환한다. |
int read(byte[] b) |
배열 b의 크기만큼 읽어서 배열을 채우고 읽어 온 데이터의 수를 반환한다. |
int read(byte[] b, int off, int len) |
최대 len 개의 byte를 읽어서 배열 b의 위치off부터 저장한다. |
void reset() |
스트림에서 위치를 마지막으로 mark()이 호출되었던 위치로 되돌린다. |
long skip(long n) |
스트림에서 주어진 길이n만큼 건너뛴다. |
OutputStream 메서드
메서드 명 |
설 명 |
void close() |
입력소스를 닫음으로써 사용하고 있던 자원을 반환한다. |
void flush() |
스트림의 버퍼에 있는 모든 내용을 출력소스에 쓴다. |
abstract void write(int b) |
주어진 값을 출력소스에 쓴다. |
void write(byte[] b) |
주어진 배열 b에 저장된 모든 내용을 출력소스에 쓴다. |
void write(byte[] b, int off, int len) |
주어진 배열 b에 저장된 내용 중에서 off번째 부터 len개 만큼만을 읽어서 출력소스에 쓴다. |
2.2 ByteArrayInputStream과 ByteArrayOutputStream
두 스트림은 메모리, 즉 바이트배열에 데이터를 입출력하는데 사용되는 스트림이다. 주로 다른 곳에 입출력하기전에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업을 하는데 사용된다.
자주 사용하지는 않지만 스트림의 종류가 달라도 읽고 쓰는 방법은 동일하므로 예제를 통해서 스트림에 읽고 쓰는 방법을 잘 익혀두기 바란다.
public class Main {
public static void main(String[] args) {
byte[] inSrc = {0,1,2,3,4,5,6,7,8,9};
byte[] outSrc = null;
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
input = new ByteArrayInputStream(inSrc);
output = new ByteArrayOutputStream();
int data = 0;
while((data = input.read()) != -1) {
output.write(data);
}
outSrc = output.toByteArray();
System.out.println("Input Source: " + Arrays.toString(inSrc));
System.out.println("output Source: " + Arrays.toString(outSrc));
}
}
출력 결과:
Input Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Input Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
ByteArrayInputStream ByteArrayOutputStream을 이용해서 바이트배열 inSrc의 데이터를 outSrc로 복사하는 예제이다.
하지만 이 코드는 while문 한번에 1 byte만 읽고 쓰므로 작업효율이 떨어진다.
public class Main {
public static void main(String[] args) {
byte[] inSrc = {0,1,2,3,4,5,6,7,8,9};
byte[] outSrc = null;
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
input = new ByteArrayInputStream(inSrc);
output = new ByteArrayOutputStream();
int data = 0;
while((data = input.read()) != -1) {
output.write(data);
}
outSrc = output.toByteArray();
System.out.println("Input Source: " + Arrays.toString(inSrc));
System.out.println("output Source: " + Arrays.toString(outSrc));
}
}
출력 결과:
Input Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
output Source: [5, 6, 7, 8, 9]
int read(byte[] b, int off, int len)와 void write(byte[] b, int off, int len)을 이용해서 입출력하는 방법을 보여주는 예이다.
이전 예제와는 달리 byte배열을 이용해서 한 번에 배열의 크기만큼 읽고 쓸 수 있다.
만약 스트림의 크기를 정확히 모를 때는 일정한 크기의 바이트를 계속해서 받아서 써야한다.
public class Main {
public static void main(String[] args) {
byte[] inSrc = {0,1,2,3,4,5,6,7,8,9};
byte[] outSrc = null;
byte[] temp = new byte[10];
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
input = new ByteArrayInputStream(inSrc);
output = new ByteArrayOutputStream();
input.read(temp, 0, temp.length); // 읽어 온 데이터를 temp에 담는다.
output.write(temp, 5, 5); // temp[5]부터 5개의 데이터를 write한다.
outSrc = output.toByteArray();
System.out.println("Input Source: " + Arrays.toString(inSrc));
System.out.println("temp Source: " + Arrays.toString(temp));
System.out.println("output Source: " + Arrays.toString(outSrc));
}
}
출력 결과:
Input Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp Source: [8, 9, 6, 7]
output Source: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2.3 FileInputStream과 FileOutputStream
위의 두 스트림은 파일에 입출력을 하기 위한 스트림이다.
public class Main {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
int data = 0;
while((data = fis.read()) != -1) {
fos.write(data);
}
fis.close();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
명령어:
> java -classpath <class파일 위치> Main Main.java Main.bak
이렇게 하면 Main.java 파일의 내용을 Main.bak 파일이 가지게 된다.
'Java > Java의 정석 읽고 정리' 카테고리의 다른 글
java의 정석 9강. java.lang 패키지(하) (0) | 2018.06.11 |
---|---|
java의 정석 9강. java.lang 패키지(상) (0) | 2018.06.11 |
java의 정석 8강. 예외처리(하) (0) | 2018.06.07 |
java의 정석 8강. 예외처리(상) (0) | 2018.06.03 |
java의 정석 7강. 객체지향프로그래밍2(하) (0) | 2018.06.03 |