■ I/O란?
I/O(Input/Output)은 입력과 출력으로, 간단히 입출력이라고 부른다.
입출력은 프로그램간 데이터를 주고 받는 행위를 말하는데, 기본적으로 키보드로 입력을 하는 것, 프로그래밍(System.out.println())을 통한 출력 등 컴퓨터 내부 또는 외부 장치를 통해 실현된다.
스트림은 흐름이라는 뜻이다.
입출력에 비유하자면, 입력부터 출력까지의 흐름이며, 입력이 출력까지 가는 통로(?)로 생각하면 된다.
통로라고 생각해보면, 흐름은 단방향으로 이루어 진다는 것을 알 수 있다.
만약, 여러 개를 보내게 된다면 선입선출(FIFO)방식으로 진행이 된다.
버퍼는 위의 선입선출(FIFO)과 같이 동작하지는 않는다.
버퍼라는 곳에 입력 할 데이터들을 모아두고, 한 번에 보내는 방식이다.
일종의 데이터 공간으로 메모리간, 컴퓨터와 사용자간의 속도 차이로 인해 생기는 병목현상을 줄일 수 있다.
그리고 데이터를 쌓아두고 한 번에 찾을 수 있으므로 확실히 스트림 방식보다는 속도가 향상된다.
채널을 통해서 소켓, 파일 등에 데이터를 입출력할 때 버퍼를 사용함으로써 가비지량을 최소화 시킬 수 있으며, 이는 GC(가비지 컬렉션) 회수를 줄임으로써 서버의 전체 처리량을 증가 시켜준다.
채널은 스트림과는 다르게 양방향으로 이루어지며, 채널에서 데이터를 주고 받을 때 사용되는 것이 버퍼이다.
non-blocking / 비동기 방식 모두 지원한다.
채널에는 소켓과 연결된 SocketChannel, 파일과 연결된 FileChannel, 파이프와 연결된 Pipe.SinkChannel과 Pipe.SourceChannel 등이 존재하며,
서버 소켓과 연결된 ServerSockerChannel도 존재한다.
InputStream은 입력 스트림들 중 최상위 클래스로 추상 클래스이다.
Byte 기반으로 입력되는 스트림은 InputStream 클래스를 상속받아 생성된다.
● Method
- read() : 입력 스트림으로부터 1byte를 읽고 리턴하는 메소드
- read(byte[] b) : 입력 스트림으로부터 읽은 바이트들을 byte[] b에 저장하고 읽은 바이트 수를 리턴하는 메소드
- read(byte[] b, int off, int len) : 입력 스트림으로부터 len byte만큼 읽어 byte[] b의 b[off]부터 len개수까지 저장한 후 읽은 byte 수인 len개를 리턴한다. 만약 len개보다 적은 byte를 읽는 경우 실제 읽은 byte수를 리턴하는 메소드
- close() : 사용한 시스템 리소스를 반납 후 입력 스트림을 닫는 메소드
FileInputStream을 이용해서 예제를 만들어보자
public class Main {
public static void main(String[] args) throws IOException {
try {
InputStream a = new FileInputStream("C:\\test.txt"); // txt파일에는 Hello! 가 저장
while (true) {
int i = a.read();
System.out.println("hi : " + i);
if (i == -1) break;
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
결과
hi : 72
hi : 101
hi : 108
hi : 108
hi : 111
hi : 33
hi : -1
위의 예제에서 FileInputStream을 통해 test.txt파일을 읽어와서 a에 저장한다.
while문 안에 read() 메소드를 사용했으니, 1byte씩 읽어오게 된다.
test.txt파일에는 현재 Hello! 가 저장되어 있기 때문에 총 6번 1byte씩 읽은 뒤 출력하게 된다.
저장되어 있는 데이터가 소진되어 -1이 출력되면서 종료된다.
OutputStream은 InputStream과 마찬가지로 출력 스트림들 중 최상위 클래스로 추상 클래스이다.
Byte 기반으로 출력되는 스트림은 OutputStream 클래스를 상속받아 생성된다.
● Method
- write(byte[] b) : 출력 스트림으로부터 주어진 byte[] b의 모든 byte를 보내는 메소드
- write(byte[] b, int off, int len) : 출력 스트림으로부터 byte[] b의 b[off]부터 len개 까지의 byte를 보내는 메소드
- flush() : 버퍼에 남아있는 모든 byte를 출력하는 메소드
- close() : 사용한 시스템 리소스를 반납 후 출력 스트림을 닫는 메소드
InputStream의 예제와 같이 FileInputStream을 이용해서 예제를 만들어보자
public class Main {
public static void main(String[] args) throws IOException {
OutputStream a = new FileOutputStream("C:\\Users\\KIMJAEHEE\\test.txt") ;
try {
String b = "OutputStream 예제";
byte[] c = b.getBytes();
a.write(c);
} catch (Exception e) {
e.printStackTrace();
}
}
}
결과적으로 해당 경로에 test.txt라는 파일이 생기고 "OutputStream 예제라는 텍스트가 적혀 있었다.
InputStream과 파일 경로가 다른 이유는... 단순히 C드라이브에 파일을 만들 때 엑세스가 거부 되었기 때문 -_-;;
위의 예제에서 wrtie() 메소드를 사용해서 b의 모든 바이트를 내서 한 번에 처리하였다.
Byte 스트림은 데이터를 byte 단위로 전송하며 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의 입력 스트림을 출력
Byte 스트림을 이용하여 간단한 예시를 만들어 보자
public class Main {
public static void main(String[] args) {
test();
}
public static void test(){
byte[] input = {0,1,2,3,4,5,6,7,8,9};
byte[] output;
int len;
ByteArrayInputStream inputStream = new ByteArrayInputStream(input);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {
while ((len = inputStream.read()) != -1){
outputStream.write(len);
}
output = outputStream.toByteArray();
System.out.println("output : "+ Arrays.toString(output));
inputStream.close();
outputStream.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
결과
output : [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
위의 예시에서 while문을 통해 inputStream을 끝까지 읽고, ouputStream에 저장한다.
최종적으로는 outputStream의 데이터를 output배열에 저장하여 출력을 한 것이다.
Java는 char형도 2byte이기 때문에 byte단위 스트림은 문자열을 전송하는데 적합하지 않다.
그래서 문자 기반의 스트림인 Character 스트림을 제공한다.
Class명도 뒤에 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에서 미리 정의해둔 표준 입출력 클래스이다.
표준 입출력은 콘솔화면에 입출력 된다고 해서 콘솔 입출력이라고도 한다.
java.lang 패키지의 System 클래스를 통해 제공되며, Java에서 기본적으로 생성하기 때문에 별도로 생성 할 필요가 없다.
● System.in : 표준 입력용 스트림
- System.in.read() : 키보드로 입력된 값을 읽어들임, 더 이상 읽을 수 없으면 -1을 리턴
● System.out : 표준 출력용 스트림
- System.out.write() : ()안에 입력된 값을 화면(콘솔)에 출력, 컴퓨터가 숫자로 저장하고 있는 것을 사람이 읽을 수 있는 문자로 디코딩해서 출력
- System.out.flush() : 출력은 버퍼에 일정 용량 이상이 쌓여야 가능한데, 버퍼를 비워서 바로 출력하도록 하는 메소드
● System.err : 표준 오류 출력 스트림
※ 이것들을 이용해서 키보드 문자 입력 방법에 대해 알아보자
1. Scanner
Scanner scanner = new Scanner(System.in);
Scanner 클래스를 이용해서 데이터를 받아올 수 있으며 next(), nextLine(), nextByte(), nextDouble()과 같은 메소드를 통해 읽어올 수 있다.
2. System.in.read()
위의 System.in을 설명할 때 적어 놓았듯이 read함수를 이용하여 입력을 받을 수 있다.
1byte씩 데이터를 읽어서 ascii코드에 해당하는 int형을 리턴한다.
3. BufferedReader
한 문자씩 스트림에서 읽어오는 InputStreamReader를 버퍼를 사용해서 문자열 처리를 편하게 할 수 있다.
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
String str = bufferedReader.readLine();
bufferedReader.close();
Scanner처럼 데이터 type에 맞게 파싱해주는 메소드를 제공하지 않기 때문에
Integer.parseInt()와 같은 메소드를 이용해서 형변환을 해주어야 한다.
이것은 Scanner보다 빠른 처리가 가능하다.
※ 이번엔 데이터 출력 방법에 대해 알아보자
System 메소드인 System.out.println() 등을 이용하는 방법이 있지만, 성능 저하의 원인이 될 수 있다.
따라서 BufferReader와 같은 방법으로 BufferedWriter를 이용할 수 있다.
public class Main {
public static void main(String[] args) throws IOException{
BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
bufferedWriter.write("Hello\n");
bufferedWriter.close();
}
}
// 결과 : Hello
System.out.println()처럼 자동 줄바꿈을 해주는 메소드가 없기 때문에 개행문자(\n)를 입력 해주어야 한다.
그리고 IOException 예외 처리를 해야 사용할 수 있다.
파일을 읽을 때 FileReader 객체와 BufferReader 객체가 사용된다.
public class Main {
public static void main(String[] args) throws IOException{
File file = new File("C:\\Users\\KIMJAEHEE\\Desktop\\test.txt");
try {
//입력 스트림, 입력 버퍼 생성
FileReader fileReader = new FileReader(file);
BufferedReader br = new BufferedReader(fileReader);
String line = "";
//한줄 씩 읽기
while((line = br.readLine())!=null) {
System.out.println(line);
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
결과
hello
안녕
파일 읽기 예제
123
test.txt 파일에 있는 텍스트를 정상적으로 출력했다.
위의 예제에서 입력 스트림과 입력 버퍼를 생성하고 readLine()을 통해 한 줄씩 읽었다.
파일을 쓸 때 FileWriter 객체와 BufferWriter 객체가 사용된다.
public class Main {
public static void main(String[] args) throws IOException{
try {
//파일 객체 생성
File file = new File("C:\\Users\\KIMJAEHEE\\Desktop\\test2.txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
if (file.isFile() && file.canWrite()) {
//쓰기
bw.write("파일 쓰기 예제");
bw.newLine();
bw.write("한 줄 띄우기");
bw.newLine();
bw.write("Bye!");
bw.close();
}
} catch (IOException e) {
System.out.println(e);
}
}
}
결과
// 생성된 test2 파일 내부
파일 쓰기 예제
한 줄 띄우기
Bye!
위의 결과로 test2라는 텍스트 파일이 정상적으로 생성되었다.
write()를 통해 넣을 데이터를 작성하고 newLine()을 사용해서 줄을 바꾸었다.
9주차 : 람다식 (0) | 2021.05.26 |
---|---|
9주차 : 제네릭 (0) | 2021.05.23 |
8주차 : 애노테이션 (0) | 2021.05.17 |
7주차 : Enum (0) | 2021.05.13 |
7주차 : 멀티쓰레드 프로그래밍 (0) | 2021.05.10 |