입력과 출력
컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것
EX) 키보드로 데이터 입력, System.out.println(0으로 화면에 출력
✔️ 데이터를 전달하려면 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요
✔️ 스트림이란 데이터를 운반하는데 사용되는 연결통로
✔️ 앞서 14장에서 배웠던 스트림이랑은 다른 개념
✔️ 단반향통신만 가능 따라서 입력스트림과 출력스트림 2개가 필요하다.
✔️ 중간에 데이터를 건너뛰지 않고 연속적으로 받으며 FIFO(먼저 들어온거 먼저나감) 구조
✔️ 스트림은 바이트 단위로 데이터를 전송
✔️ 입출력 대상에 따라 다음과 같은 입출력 스트림이 있다.
InputStream의 자손, 입력스트림 | OutputStream의 자손, 출력스트림 | 대상 |
---|---|---|
FileInputStream | FileOutputStream | 파일 |
ByteArrayInputStream | ByteArrayOutputStream | 메모리(Byte배열) |
PipedInputStream | PipedOutputStream | 프로세스(프로세스간의 통신) |
AudioInputStream | AudioOutputStream | 오디오장치 |
✔️ java.io패키지의 InputStream,OutputStream 추상클래스
추상클래스이기 때문에 상속받는 경우 추상메서드를 필수로 오버라이딩 해야한다.
하지만 추상메서드뿐만 아니라 추상메서드를 호출하는 읽기/쓰기 메서드들도 같이 오버라이딩 해야 한다.
read : 파란 파일을 읽어서 노란 파일에
write : 파일 파일에 쓰기 노란 파일의 내용을
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) |
FileInputStream/FileOutputStream
ByteArrayInputStream/ByteArrayOutputStream
PipedInputStream/PipedOutputStream
AudioInputStream/AudioOutputStream
외에도 스트림의 기능을 보완하기 위한 보조스트림이 제공된다.
보조스트림은
✔️ 입출력을 처리할 수 없음 따라서 스트림을 먼저 생성하고 난 다음에 이를 이용
✔️ 기능을 향상시키거나 새로운 기능을 추가
//먼저 기반스트림을 생성한다.
FileInputStream fis = new FileInputStream("test.txt");
//기반스트림을 이용해서 보조스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();
//실제 입력 : FileInputStream
// 입출력의 성능을 높이는 버퍼를 제공 : BufferedInputStream
✔️ InputStream>FilterInputStream>보조스트림 (OutputStream도 마찬가지)
입력 | 출력 | 설명 |
---|---|---|
FilterInputStream | FilterOutputStream | 필터를 이용한 입출력 처리 |
BufferedInputStream | BufferedOutputStream | 버퍼를 이용한 입출력 처리 |
DataInputStream | DataOutputStream | int,float과 같은 기본형 단위로 데이터 처리 |
SequenceInputStream | 없음 | 두 개의 스트림을 하나로 연결 |
LineNumberInputStream | 없음 | 읽어 온 데이터의 라인 번호를 카운트 |
ObjectInputStream | ObjectOutputStream | 데이터를 객체 단위로, 주로 파일을 이용하며 객체 직렬화와 관련 |
없음 | PrintStream | 버퍼를 이용하며, 추가적인 print관련 기능 |
PushbackInputStream | 없음 | 버퍼를 이용해서 읽어 온 데이터를 다시 되돌림 |
✔️ 문자 char형의 경우 2byte이기 때문에 바이트기반 스트림이 아닌 문자기반 스트림을 이용해야 한다.
InputStream ➡️ Reader
OutputStream ➡️ writer
바이트기반 스트림 | 문자기반 스트림 |
---|---|
FileInputStream FileOutputStream | FileReader FileWriter |
ByteArrayInputStream ByteArrayOutputStream | ByteArrayReader ByteArrayWriter |
PipedInputStream PipedOutputStream | PipedReader PipedWriter |
AudioInputStream AudioOutputStream | AudioReader AudioWriter |
🔍 바이트기반 스트림과 문자기반 스트림의 비교표
✔️ 메서드들을 살펴보자. 추상메서드가 달라졌다.
InputStream | Reader |
---|---|
abstractint read() | int read() |
int read(byte[] b) | int read(char[] cbuf) |
int read(byte[] b, int off, int len) | abstract int read(char[] cbuf, int off, int len) |
OutputStream | Writer |
---|---|
abstractint write() | int write() |
int write(byte[] b) | int write(char[] cbuf) |
int write(byte[] b, int off, int len) | abstract int write(char[] cbuf, int off, int len) |
void write(String str) | |
void write(String str,int off, int len) |
🔍 바이트기반 스트림과 문자기반 스트림의 읽고 쓰는 메서드 비교
✔️ 보조스트림도 바뀌었기 때문에 살펴보자.
바이트기반 보조스트림 | 문자기반 보조스트림 |
---|---|
FilterInputStream FilterOutputStream | FilterReader FilterWriter |
BufferedInputStream BufferedOutputStream | BufferedReader BufferedWriter |
DataInputStream DataOutputStream | 없음 |
SequenceInputStream | 없음 |
LineNumberInputStream | LineNumberReader |
ObjectInputStream ObjectOutputStream | 없음 |
PrintStream | PrintWriter |
PushbackInputStream | PushbackReader |
메서드명 | 설명 |
---|---|
void close() | 스트림을 닫고 사용하고 있던 자원 반환 |
void mark(int readlimit) | 현재 위치를 표시 후에 reset()에 의해서 이 위로 다시 돌아갈 수 있다. readlimit은 되돌아갈 수 있는 byte수 |
boolean markSupported() | mark()와 reser()을 지원하는지 알려줌 |
void reset() | 스트림에서의 위치를 마지막으로 mark()이 호출되었던 위치로 되돌린다. |
int available() | 스트림으로부터 읽어 올 수 있는 데이터의 크기 반환 |
long skip(long n) | 스트림의 주어진 길이만큼 건너뛴다 |
🔍 InputStream의 메서드
mark()와 reset()을 통해서 이미 읽은 데이터를 되돌려서 다시 읽을 수 있다.
이 기능을 지원하는 스트림인지는 markSupported()를 통해서 확인가능하다.
또한 앞서 배웠던 read()메서드들은 생략했다.
메서드명 | 설명 |
---|---|
void close() | 스트림을 닫고 사용하고 있던 자원 반환 |
void flush() | 스트림의 버퍼에 있는 모든 내용을 출력소스에 쓴다 |
🔍 OutputStream의 메서드
flush()는 버퍼가 있는 출력스트림의 경우에만 의미가 있게 작동하고
그 외에는 아무런 일도 하지 않는다.
close()를 통해서 스트림을 사용해서 모든 작업을 마치고 난 후에 반드시 닫아주기로 한다.
그러나 ByteArrayInputStream과 같이 메모리를 사용하는 스트림과
System.out,System.in과 같은 표준 입출력 스트림은 닫아주지 않아도 된다.
✔️ 메모리 즉 바이트배열에 데이터를 입출력 하는데 사용되는 스트림
import java.io.*;
import java.util.Arrays;
class IOEx1 {
public static void main(String[] args) {
// 바이트 배열 inSrc의 데이터를 outSrc로 복사하는 예제
byte[] inSrc= {1,2,3,4,5,6,7,8,9};
byte[] outSrc = null;
ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int data =0;
while((data=input.read())!=-1) { //더 이상 읽어올 수 없으면 -1을 반환, 1byte씩 읽는다.
output.write(data);
}
outSrc = output.toByteArray(); //스트림의 내용을 Byte배열로 반환
System.out.println("Input Source :"+Arrays.toString(inSrc));
System.out.println("Output Source :"+Arrays.toString(outSrc));
}
}
👆 1byte씩 읽고 쓰므로 이보다 더 효율적인 방법을 살펴보자.
while((data=input.read())!=-1) { //더 이상 읽어올 수 없으면 -1을 반환, 1byte씩 읽는다.
output.write(data);
}
import java.io.*;
import java.util.Arrays;
class IOEx2 {
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 = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
input.read(temp,0,temp.length);
//tmep.read(output,5,5);
output.write(temp,5,5);
outSrc = output.toByteArray(); //Arrays 로 바꿔서 toString함수를 이용해서 출력하기
System.out.println("Input Source :"+Arrays.toString(inSrc));
System.out.println("temp :"+Arrays.toString(temp));
System.out.println("Output Sorce :"+Arrays.toString(outSrc));
}
}
👆 int read(byte[] b,int off,int len)와 void write(byte[] b,int off,int len)를 이용해서 입출력하는 예제
import java.io.*;
import java.util.Arrays;
class IOEx3 {
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[4];
ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
System.out.println("Input Source :"+Arrays.toString(inSrc));
try {
while(input.available()>0) {
input.read(temp);//temp크기 만큼 데이터를 읽어와 temp배열에 저장하고 읽어온 데이터 수 반환
output.write(temp);//temp 에서 읽어오 모든 내용을 출력
outSrc = output.toByteArray();
printArrays(temp,outSrc);
}
}catch(IOException e) {}
}
static void printArrays(byte[] temp, byte[] outSrc) {
System.out.println("temp :"+Arrays.toString(temp));
System.out.println("Output Source :"+Arrays.toString(outSrc));
}
}
👆 하나하나 살펴보자
1) try-catch문을 한 이유는 read()나 write()가 IOException인 예외를 불러올 수 있기 때문에
2) available() 메서드는 데이터 입력될 때까지 기다리는 블록킹 없이 읽어올 수 있는 바이트 수를 반환
3) int read(byte[] b) 메서드는 b 크기만큼 데이터를 읽어와서 b에 그 데이터를 저장한다.
4) 3회차 while에서 input Source에서 읽어올 수 있는 데이터가 8,9 두 개여서 그 데이터 두개를 temp배열 4567에 그대로 붙여서 8967이 되는 것이다.
5) 따라서 이 8967이 그대로 ourSrc에 붙어버린다.
다음 예제에서는 이를 해결해보자. 8967에 그대로 붙는게 아니라 temp가 inSrc에서 읽어온 갯수만큼만 outSrc에 붙이도록 하겠다.
이는 사실 간단하다 int read(byte[] b)메서드가 읽어온 데이터의 수를 반환하기 때문에 이 수를 그래도 이용하기로 한다.
파일 입축력을 하기 위한 스트림
import java.io.*;
class FileViewer {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream(args[0]);
int data = 0;
while((data=fis.read())!=-1) {
char c =(char) data;
System.out.print(c);
}
}
}
👆 char c =(char) data; 이렇게 형변환이 있는데 read()메서드가 1byte씩 읽어오기 때문에 2byte인 char로 형변환 하는 것이 가능하다. (정보 손실 없음)
import java.io.*;
class FileCopy {
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();
}
}
}
중간에 띄어쓰기를 해서 두개의 인자를 구별
👆 FileCopy.java 파일을 읽어서 File.txt 파일에 출력하는 예제이다.
✔️ InputStream/OutputStream>FilterInputStream/FilterOutputStream>모든 보조스트림의 조상
✔️ 보조스트림은 자체적으로 입출력 수행할 수 없으면 기반 스트림이 필요하다.
protected FilterInputStream(InputStream in)
public FilterOutputStream(OutputStream out)public class FilterInputStream extends InputStream{//InputStream상속 protected volatile InputStream in; protected FilterInputStream(InputStream in){ this.in=in; } public int read() throws IOException{ //InputStream의 추상메서드 오버라이딩 return in.read(); } ... }
여기서 보면 FilterInputStream의 생성자가 protected이기 때문에
직접 생성자를 생성하여 사용할 수 없고 이 FilterInputStream을 상속받아서
오버라이딩되어햐 한다.
FilterInputStream/FilterOutputStream은 모든 보조스트림의 조상이기 때문에 직접 생성자를 생성하지 않고 보조스트림을 통해서 생성하게 될 것이다.
버퍼는 수례이다.
보통 바이트 기반으로 하게 되면 1byte만 담을 수 있는 바구니로 여러번 이동해야 하지만 버퍼는 수례이기 때문에 정해진 용량을 채워서 옮긴다. 따라서 입출력 과정에서 더 효율적이다.
BufferedInputStream과 BufferedOutputStream의 생성자를 알아보자
BufferedInputStream 생성자 | 설명 |
---|---|
BufferedInputStream(InputStream in, int size) | 주어진 InputStream의 인스턴스를 입력소스로 하며 지정된 크기(byte단위)의 버퍼를 갖는 BufferedInputStream를 생성 |
BufferedInputStream(InputStream in) | 버퍼의 크기가 지정해주지 않으면 8192byte 크기의 버퍼를 갖는다. |
버퍼는 수례라고 했다.
BufferedInputStream의 경우 InputStream으로부터 한번에 읽어 올수 있는 데이터의 크기라고 볼 수 있다. 따라서 앞서 read()를 할 때 1byte씩 읽었다면 이제는 1byte가 아닌 그 이상으로 한번에 읽어오는 것이 가능해진것이다.
BufferedOutputStream 생성자 | 설명 |
---|---|
BufferedOutputStream(InputStream in, int size) | 주어진 OutputStream의 인스턴스를 출력소스로 하며 지정된 크기(byte단위)의 버퍼를 갖는 BufferedOutputStream를 생성 |
BufferedOutputStream(InputStream in) | 버퍼의 크기가 지정해주지 않으면 8192byte 크기의 버퍼를 갖는다. |
flush() | 버퍼의 모든 내용을 출력소스에서 출력->버퍼 비우기 |
close() | flush()->BufferedOutputStream인스턴스가 사용한 모든 자원 반환 |
중요한 것은 버퍼가 가득차면 모든 내용을 출력한다는 것!
따라서 버퍼가 가득차지 않으면 출력을 하지 않기 때문에
마지막 출력 부분이 지정된 버퍼 크기보다 작다면 계속 내부 버펴에 남아있는 상태에서 프로그램이 종료될 수도 있다.
따라서 모든 출력을 마친 후에 close()나 flush를 호출해서 마지막에 버퍼에 남아있을지도 모르는 내용을 출력소스에 출력한다.
import java.io.*;
class BufferedOutputStreamEx1 {
public static void main(String[] args) {
try {
FileOutputStream fos = new FileOutputStream("File.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos,5);
for(int i='1';i<='9';i++) {
bos.write(i);
}
fos.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
👆 버퍼 크기가 5이기 때문에 4byte가 채워진 버퍼는 출력되지 못한 채 종료되었다.
👆 fos.close()는 버퍼를 닫는 것이라 아니라 FileOutputStream의 자원을 반환하는 것이므로 bos.close()를 통해서 버퍼에 남아있던 내용을 출력하고 반환해야 한다.
FilterInputStream/FilterOutputStream > DataInputStream/DataOutputStream
- 데이터를 읽고 쓰는데 있어서 byte단위가 아닌, 8가지 기본 자료형 단위로 읽고 쓸 수 있다.
- 출력하는 형식은 16진수로 표현하여 저장
import java.io.*;
class DataOutputStreamEx1 {
public static void main(String[] args) {
FileOutputStream fos = null;
DataOutputStream dos = null;
try {
fos = new FileOutputStream("File.txt");
dos = new DataOutputStream(fos);
dos.writeInt(10);
dos.writeFloat(20.0f);
dos.writeBoolean(true);
dos.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
👆 위와 같이 알 수 없는 글자로 이루어진 파일
import java.io.*;
import java.util.Arrays;
class DataOutputStreamEx2 {
public static void main(String[] args) {
ByteArrayOutputStream bos = null;
DataOutputStream dos = null;
byte[] result = null;
try {
bos = new ByteArrayOutputStream();
dos = new DataOutputStream(bos);
dos.writeInt(10);
dos.writeFloat(20.0f);
dos.writeBoolean(true);
result = bos. toByteArray();
String[] hex = new String[result.length];
for(int i =0;i<result.length;i++) {
if(result[i]<0) {
hex[i] = String.format("%02x", result[i]+256);
}else {
hex[i] = String.format("%02x", result[i]);
}
}
System.out.println("10진수 :"+Arrays.toString(result));
System.out.println("16진수 :"+Arrays.toString(hex));
dos.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
👆 이 예제는 좀 자세히 봐야한다.
1) 일단 컴퓨터는 값을 2진수로 저장한다.
2) 자료형이 int는 4byte인 32bit를 갖는다. 10을 넣으면 이진수로
00000000 00000000 00000000 00001010 로 저장할 것이다.
따라서 10진수의 출력 결과는 0 0 0 10이다
3) 자료형이 float는 4byte인 32bit를 갖는다.
하지만 실수형은 부동소수점을 이용하여 값을 저장한다. 따라서
0(부호부분 1bit) 00000000(지수부분8bit)00000000000000000000000(가수부분 23bit) = 총 32bit의 형태로 값을 저장한다.
이 예시의 경우 20.0f를 넣는다.
자 이제 20을 부동소수점으로 표현해보자
1) 20을 이진수로 표현한다. ➡️ 10100(2), 양수이므로 부호는 0으로 채워진다.
2) 이제 가수와 지수를 생성해보자 10100(2)= 1.0100 x 2의 4승
3) 가수는 0100 가수부분의 왼쪽부터 채운다.
4) 지수는 4인데 Bias인 127을 더해서 131이 된다.
131의 2진수는 ➡️ 10000011(2)
따라서 65 -95 0 0이 출력되는 것이다.
5) boolean의 경우 1byte의 자료형이며 값이 0(false),1(true)로 저장되기 때문에 1이 저장되었다.
6) 부호없는 1byte의 범위는 0~255이므로 255f를 더해서 16진수의 정수로 표현
import java.io.*;
class DataInputStreamEx1 {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("File.txt");
DataInputStream dis = new DataInputStream(fis);
System.out.println(dis.readInt());
System.out.println(dis.readFloat());
System.out.println(dis.readBoolean());
dis.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
👆 저 알 수 없는 글자 파일이 이렇게 읽혔다.
문자로 저장되었지만 DataInputStream을 이용하여 숫자로 읽혔다.
변환의 과정이나 자리수를 세지 않아도 되어 편리하다.
import java.io.*;
class DataOutputStreamEx3 {
public static void main(String[] args) {
int[] score = {100,90,95,85,50};
try {
FileOutputStream fos = new FileOutputStream("score.dat");
DataOutputStream dos = new DataOutputStream(fos);
for(int i=0;i<score.length;i++) {
dos.writeInt(score[i]);
}
dos.close();
}catch(IOException e) {
e.printStackTrace();
}
}
}
👆진짜 dat형태의 이상한 사진편집기 파일이 생겼지만
내용을 확인하기 위해서 score.txt로 바꾸었더니 저렇게 문자로 저장되었다.
실제로 저장된 내용은 100,90,95,85,50일 것이다.
import java.io.*;
class DataInputStreamEx2 {
public static void main(String[] args) {
int sum =0;
int score =0;
FileInputStream fis = null;
DataInputStream dis =null;
try {
fis= new FileInputStream("score.txt");
dis = new DataInputStream(fis);
while(true) {
score=dis.readInt();
System.out.println(score);
sum+=score;
}
}catch(IOException e) {
System.out.printf("점수의 총합은 %d점 입니다",sum);
}finally {
try {
if(dis!=null)
dis.close();
}catch(IOException ie) {
ie.printStackTrace();
}
}
}
}
위 파일을 읽으면
위의 결과가 나온다.
👆while(true)여서 무한반복이다. 따라서 예외처리에서 배웠지만 finally는 무조건 해야하는 예외처리이기 때문에 여기서 스트림을 닫는다.
try문 중간에 예외가 발생하여 catch문으로 가버리면 닫지 못한 상태이기 때문에 finally문을 추가하여 닫는 것이 더 확실한 방법이다.
➡️ JDK1.7부터 close()를 호출하지 않아도 자동으로 호출되도록 할 수 있다.
try-with-resource문을 이용하는 것!
아래 예시는 try-with-resource문을 이용할 것이다.
import java.io.*;
class DataInputSteamEx3 {
public static void main(String[] args) {
int sum=0;
int score =0;
try (FileInputStream fis = new FileInputStream("score.txt");
DataInputStream dis = new DataInputStream(fis)){
while(true) {
score=dis.readInt();
System.out.println(score);
sum+=score;
}
}catch(EOFException e) {
System.out.printf("점수의 총합은 %d점입니다",sum);
}catch(IOException ie) {
ie.printStackTrace();
}
}
}
- 여러 개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리할 수 있도록 도와준다.
- 큰 파일을 여러 개의 작은 파일로 나누었다가 하나의 파일로 합치는 것과 같은 작업을 수행할 때 사용하면 좋다.
메서드/생성자 | 설명 |
---|---|
SequenceInputStream(Enumeration e) | Enumeration에 저장된 순서대로 입력스트림을 하나의 스트림으로 연결 |
SequenceInputStream(InputStream s1, InputStream s2) | 두 개의 입력스트림을 하나로 연결 |
[1] SequenceInputStream(Enumeration e)의 사용 예시
Vector files = new Vector();
files.add(new FileInputStream("file.001"));
files.add(new FileInputStream("file.002"));
SequenceInputStream in = new SequenceInputStream(files.elements());
[2] SequenceInputStream(InputStream s1, InputStream s2)의 사용 예시
FileInputStream file1 = new FileInputStream("file.001"));
FileInputStream file2 = new FileInputStream("file.002"));
SequenceInputStream in = new SequenceInputStream(file1,file2);
import java.io.*;
import java.util.*;
class SequenceInputStreamEx {
public static void main(String[] args) {
byte[] arr1 = {0,1,2};
byte[] arr2 = {3,4,5};
byte[] arr3 = {6,7,8};
byte[] outSrc = null;
Vector v = new Vector();
v.add(new ByteArrayInputStream(arr1));
v.add(new ByteArrayInputStream(arr2));
v.add(new ByteArrayInputStream(arr3));
SequenceInputStream input = new SequenceInputStream(v.elements());
ByteArrayOutputStream output = new ByteArrayOutputStream();
int data =0;
try {
while((data=input.read())!=-1) {
output.write(data);
}
}catch(IOException e) {}
outSrc = output.toByteArray();
System.out.println("Input Source1 :"+Arrays.toString(arr1));
System.out.println("Input Source2 :"+Arrays.toString(arr2));
System.out.println("Input Source3 :"+Arrays.toString(arr3));
System.out.println("Output Source :"+Arrays.toString(outSrc));
}
}
- 데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print,println,printf와 같은 메서드를 오버로딩하여 제공
<오버로딩 : 같은 클래스 내에 같은 이름이지만 매개변수가 다른>
<오버라이딩: 상속 받은 클래스가 조상클래스의 메서드를 다시 재정의 하는 것>- JDK1.1부터 PrintStream보타 향상된 기능의 문자기반 스트림인 PrintWriter이 추가 -> 이것을 사용하는 것이 좋다.
✔️ print vs. println vs. printf
print 줄바꿈 안함(커서는 바로 옆에 존재) / ("바보"+i) 이렇게 자료 연결
println 줄바꿈(다음 줄로 커서 이동) / ("바보"+i) 이렇게 자료 연결
printf 줄바꿈 안함 (하고 싶을 때는 %n)/("바보 %d",i) 이렇게 자료 연결
✔️ 처음 개념을 접하는 생성자와 메서드들 정리해보자.
메서드/생성자 | 설명 |
---|---|
boolean checkError() | 스트림을 flush하고 에러가 발생했는지 알려준다. |
protected void setError() | 작업 중에 오류가 발생했음을 알린다. setError()호출->checkError()호출->return true |
PrintStream(File file) PrintStream(File file, String csn) PrintStream(OutputStream out) PrintStream(OutputStream out, boolean autoFlush, String encoding) PrintStream(String fileName) PrintStream(String fileName,String csn) | 지정된 출력스트림을 기반으로 하는 PrintStream 인스턴스를 생성한다. autoFlush =true 이면 println메서드를 호출 or 개행문자가 출력될 때 자동으로 flush. 기본값을 false |
✔️println()이나 print()는 자주 사용하는 메서드이기 때문에 내부적으로 예외를 처리할 수 있도록 try-catch문이 정의되어져 있다.
왜냐하면 사용할 때마다 try-catch문을 하기에 번거롭기 때문에
✔️ printf() 메서드는 형식화된 출력을 지원
여러가지 옵션들이 존재하고 자세한 부분은 Formatter클래스에서 참고하자
몇 가지 생소한 format에 대해서 정리해보자
정수 관련 format | 설명 | 결과 |
---|---|---|
%c | 문자 | A |
%5d | 5자리 숫자, 빈자리는 공백 | ___65 |
%-5d | 5자리 숫자, 왼쪽 정렬, 빈자리는 공백 | 65___ |
%05d | 5자리 숫자, 빈자리는 0으로 | 00065 |
실수 관련 format | 설명 | 결과 |
---|---|---|
%3.1f | . :이렇게 소수점 포함 최소 3자리,소수점 이하 1자리만(두번째 자리에서 반올림) | 1234.6 |
%8.1f | __._ :이렇게 소수점 포함 최소 8자리, 소주점 이하 1자리 | __1234.6 |
%08.1f | 000000.0:나머지 자리는 0으로 | 001234.6 |
-8.1f | __._:왼쪽 정렬 | 1234.6 |
<코드 넣기>
바이트기반 스트림의 조상이 InputStream/OutputStream 인 것처럼
문자기반 스트림의 조상은 Reader/Writer이다.
InputStream/OutputStream와 메서드는 다르지 않다 다만 매개변수로 char형태의 배열을 받는다
✔️ 바이트기반 스트림은 1byte씩 읽기 때문에 2byte인 문자의 입출력을 위해서 문자기반 스트림이 이를 처리 + 추가로 문자를 처리하기 위한 encoding
인코딩을 가볍게 설명하자면
사람의 문자를 컴퓨터가 이해하는 바이트 코드로 바꾸는 것이다.
즉 문자-> 2진수의 숫자로 변환하는 과정
✔️ Reader : 특정 인코딩 읽기-> 유니코드로 변환
Writer : 유니코드->특정 인코딩으로 변환하여 저장
- 파일로부터 텍스트데이터를 읽고 파일에 쓰는데 사용
- FileInputStream,FileOutputStream과 다르지 않다. 다만, 다루는 데이터가 다르다.
import java.io.*;
class FileREaderEx1 {
public static void main(final String[] args) {
try {
final String fileName = "test.txt";
final FileInputStream fis = new FileInputStream(fileName);
final FileReader fr = new FileReader(fileName);
int data = 0;
while ((data = fis.read()) != -1) {
System.out.print((char) data);
}
System.out.println();
fis.close();
while ((data = fr.read()) != -1) {
System.out.print((char) data);
}
System.out.println();
fr.close();
} catch (final IOException e) {
e.printStackTrace();
}
}
}
👆FileInputStream으로 읽어서 화면에 출력한 결과 데이터가 깨진 것을 볼 수 있다.
import java.io.*;
class FileConversion {
public static void main(String[ ] args){
try{
FileReader fr = new FileReader("conversion.txt");
FileWriter fw = new FileWriter("conversion2.txt");
int data =0 ;
while((data=fr.read())!=-1){
if(data!='\t'&&data!='\n'&&data!=' '&&data!='\r')
fw.write(data);
}
fr.close();
fw.close();
}catch(IOException e){
e.printStackTrace();
}
}
}
👆 입력스트림으로 읽어온 데이터에 공백을 없애는 파일을 생성하였다.
- 쓰레드 간 데이터 주고받을 때 사용
- 입력과 출력스트림을 하나의 스트림으로 연결해서 데이터를 주고 받는다.
① 스트림생성
② connerct()호출하여 InputStream과 OutputStream 연결하기
③ 한쪽 stream 닫아도 자동으로 닫아짐
import java.io.*;
class PipedReaderWriter {
public static void main(final String[] args) {
// ① Stream 생성
final InputThread inThread = new InputThread("InputThread");
final OutputThread outThread = new OutputThread("OutputThread");
// ③ connect() 호출하여 연결
inThread.connect(outThread.getOutput());
inThread.start();// run()을 구현하기 위한
outThread.start();
}
}
class InputThread extends Thread {
PipedReader input = new PipedReader();
StringWriter sw = new StringWriter();
InputThread(String name) {
super(name);// Thread(String name) Thread이름이name인 쓰레드 객체 생성
}
public void run() {// Thread의 메서드 오버라이딩
// Thread는 PipedReader 를 읽어서 StringWriter에 출력한다.
try {
int data = 0;
while ((data = input.read()) != -1) {
sw.write(data);
}
System.out.println(getName() + "received: " + sw.toString());
} catch (final IOException e) {}
}
public PipedReader getInput() {
return input;
}
public void connect(PipedWriter output) {
try {
input.connect(output);
} catch (IOException e) {}
}
}
class OutputThread extends Thread {
PipedWriter output = new PipedWriter();
OutputThread(String name) {
super(name);// Thread(String name) Thread이름이name인 쓰레드 객체 생성
}
public void run() {// Thread의 메서드 오버라이딩
// Thread는 PipedWriter에 메세지 Hello를 출력한다.
try {
String msg = "Hello";
System.out.println(getName() + " sent : " + msg);
output.write(msg);
output.close();
} catch (final IOException e) {}
}
public PipedWriter getOutput() {
return output;
}
public void connect(PipedReader input) {
try {
output.connect(input);
} catch (final IOException e) {}
}
}
CharArrayReader/CharArrayWriter와 같이 입출력 대상이 메모리인 스트림
StringWriter에 출력되는 테이터는 내부의 StringBuffer에 저장된다.SringBuffer getBuffer() : StringWriter에 출력한 데이터가 저장된 StringBuffer를 반환
String toString() : StringBuffer에 출력된(저장된) 문자열을 반환
import java.io.*;
class StringReaderWriter{
public static void main(String[] args){
String inputData = "ABCD";
StringReader input = new StringReader(inputData);
StringWriter output = new StringWriter();
int data =0;
try{
while((data=input.read())!=-1){
output.write(data);
}
}catch(IOException e){}
System.out.println("Input Data : "+inputData);
System.out.println("Output Data : "+output.toString());
// System.out.println("Output Data : "+output.getBuffer().toString());
}
}