728x90
1.입출력(I/O)과 스트림(stream)
- 원리
- 외부와 자바프로그램간의 data를 주고 받는 과정 필요
- data를 주고 받기 위해 외부와 자바프로그램을 연결시켜주는 다리가 필요 : stream
- stream은 14장의 Stream과 다르다.
- 연결시켜주는 다리(일방통행) : Input, Output
- 외부에서 자바로 들어가는 것 하나 : Input
- 자바에서 외부로 보내는 것 하나 : Output
- 목적
- 외부에서 데이터를 받아서 읽기
- 내부에서 데이터 작성해서 외부로 보내기
- input과 output은 따로따로 이다. 연결해서 작동하는 경우가 많은 거지 서로 연결할 필요가 없다.
- read(), write()
input과 output stream의 다른 method들이 분명 더 있겠지만 읽고 쓰기가 기본 원리이다.
- 외부에서 온 데이터 읽고 싶을 때 : read()
- Inputstream이 외부데이터를 먼저 받는다. -> 객체
- 해당 객체로부터 read()해서 java가 읽을 수 있도록 해준다.
- 내부에서 작성한 데이터 외부로 보낼 때 : write()
- Outputstream을 먼저 생성한다. -> 객체
- 해당 객체에 write()를 이용해서 외부로 보내고 싶은 데이터를 넣는다.
- 자바에서 작성하는 것자체가 write 아닌가?
- 내부에서 작성한 데이터는 자바프로그램이 읽을 수 있는거지 외부의 형태(동영상, 노래 파일 등등)로 읽힌다는 보장이 없다.
- 그래서 내부에서 작성한 데이터를 outputstream에 맞게 다시 write()해줘야한다.
- 자바에서 작성하는 것자체가 write 아닌가?
- 외부에서 온 데이터 읽고 싶을 때 : read()
더보기
inputStream(inputData 받기) - 데이터 형태
read로 읽음 - inputstream의 데이터 존재 여부 체크 -1, 0
- read로 한번 데이터 정리 - 필요데이터만 뽑아온다던지 -> 이방식으로 실제 input 데이터를 볼수 있음
write으로 output stream에 보낼 데이터 넣어줌 - 이상하게 생김
output으로
2. 흐름
- Byte기반stream : FileStream, Byte[]Stream, PipedSteam, AudioStream, StringBufferStream ... - Input,Output 생략
- method: read(), write()가 핵심
- 보조 stream : Filter, Buffer, LineNumber, Print, pushBack, sequence ...
- Char 기반stream : FileReader,Writer , Char[] , String , Piped ...
reader = Input
writer = Output
Byte[] -> Char[]
- method: read(), write()가 핵심
- 보조 stream : Filter, Buffer, LineNumber, Print, pushBack ...
- 기타 Stream
- InputStreamReader, OutputStreamReader : byteStream에서 charStream으로 변경
- 표준 Stream : System.out, System.in ... -> 객체 생성없이 사용 가능
- 직렬화 Stream : object를 stream에 넣을 수 있도록 해줌
- InputStreamReader, OutputStreamReader : byteStream에서 charStream으로 변경
- File
- stream에 들어가는 data 형태 중 하나
- 매우 자주 사용되므로 알아둘 필요 존재
3. Byte 기반 stream
- byte로 data를 해결
- InputStream에 data를 저장할 때 byte 형태로 저장
- OutputStream에 data write()할 때 params로 byte를 받아서 외부로 데이터 보내고 해당 데이터를 변해야되는 data type으로 변경(음악, 파일, 영상 등등) - 알아서 해줌
3.1. 메서드
// InputStream
int available() // 스트림으로부터 읽어 올 수 있는 데이터 크기를 반환
long skip(long n) // 스트림에서 주어진 길이만큼 건너뜀
boolean markSupported() //mark와 reset을 지원하는지 확인해줌
void mark(int readlimit) // 현재위치를 표시, reset()으로 해당 위치로 돌아갈 수 잇다. params는 되돌아 갈 수 있는 수
void reset() // 마지막 mark가 호출된 곳으로 되돌아감
abstract int read() // 1byte를 읽어온다. 없으면 -1 반환
int read(byte[] b) // 배열 b 길이 먼저 읽고 b배열 길이만큼 data 반환
int read(byte[] b, int off, int len) // 배열 b의 지정된 위치 off부터 len개수만큼 저장
void close() // 스트림을 닫음으로써 사용하고 있던 자원을 반환
// OutputStream
void flush()// 스트림의 버퍼에 있는 모든 내용을 출력소스에 사용
abstract void write(int b) // 주어진 값을 출력소스에 사용
boid write (byte[] b) // 주어진 배열 b에 저장된 모든 내용을 출력소스에 사용
void write(byte[] b, inf off, int len) // 주어진 배열 b에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용
void close() // 입력소스를 닫음으로써 사용하고 있던 자원을 반환
3.2. 사용 예
byteArrayStream으로 예시
- 요약
- 외부 src를 byteInput에 담는다. = input
- input을 read()를 통해서 하나하나를 읽는다
- read()는 byte 한개씩 읽고 한번 read()할 때 마다 사라진다.
- read()를 통해서 하나씩 읽은 data를 어떻게 사용할지가 관건이다.
- write에 사용할수있고
- 그냥 print()할 때 쓸수 있고
- 사용 목적에 따라 달라진다.
- read()를 통해 나온 낱개 낱개를 write에 넣어준다. - output
- output에 저장된 data를 outSrc에 담아준다.
- output이나 input 같은 경우는 컴퓨터가 읽을 수 있는 0/1의 형태에 맞게 변경되어서 read(), write()하는 것이다.
- 따라서 read(), write()만 하면 컴퓨터가 전송할 수 있는 형태가 된 것이고 == 다른 컴퓨터나 파일로 이동가능한 상태
- 사람이 읽을려면 그에 맞는 변경이 필요하다.
- stream 인스턴스에 담긴 data는 stream에 맞게 저장되어있는 것이다.
- 따라서 예제라서 한정이 된것이지만 write해서 쓴 것을 사람이 볼수 있게 볼 때, 그리고 보는 곳이 ide이기 때문에 byte[]로 변경해서 보는 것이다.
class Try1 {
public static void main(String[] args) {
byte[] inSrc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
byte[] outSrc = null; // 출력한 데이터를 받을 공간이 필요
// 외부로 부터 input을 java 프로그램이 받음 -> byte로 저장
ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int data = 0;
while ((data = input.read()) != -1) {
output.write(data); // write로 read한 데이터 사용 -> input에서는 데이터가 하나씩 사라짐
}
System.out.println("input.read() = " + input.read()); // -1 나옴
outSrc = output.toByteArray(); // 현재 outputStream에 저장한 data를 읽을 수 있게 byte[]에 넣는 과정
System.out.println("Arrays.toString(inSrc) = " + Arrays.toString(inSrc));
// Arrays.toString(inSrc) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
// Arrays.toString(outSrc) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
System.out.println("input = " + input);
// input = java.io.ByteArrayInputStream@372f7a8d
System.out.println("output = " + output);
//output = nullKVOWKGBO // input에서 객체 나온거나 output에서 이상한거 나온거나 동일하다고 보면됨
}
}
class Practice {
public static void main(String[] args) {
byte[] inSrc ={0,1,2,3,4,5,6, 'a'};
byte[] outSrc;
// 출력stream의 값을 넣을 공간을 직접 만들어 준것
// file 같은 경우에는 new FileOutPutStream("출력 stream 값을 넣을 수 있게 file 명 작성하면 file 생성됨")
byte[] temp = new byte[10];
ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
int read = input.read(); // 이미 하나 써버림
input.read(temp, 0, temp.length);
// temp로 값 넣어주면서 input의 배열은 준거만큼 삭제됨
// temp 배열에 0부터 temp.length 개까지 data를 넣는다.
// 만약 temp에서 필요로하는 index 개수가 input의 개수 이상이면 0을 저장
output.write(temp, 5, 5);
// 출력하려고 출력스트림에 값을 넣어줌 - temp는 byte[]배열이므로 삭제는 안됨
// byteStream이라서 그런지 byte형식이 직접 존재하면 write()의 params에 넣어도 상관 없는 거 같다.
outSrc = output.toByteArray();
// 출력stream의 값을 넣을 공간인 outSrc에 넣어줌
// file같은 경우는 만든 파일 속에 출력stream값이 byte형태에서 string이든 뭐든 알아서 변경되서 작성됨
// bytestream같은 경우 직접 변경해줘야함
System.out.println("Arrays.toString(in) = " + Arrays.toString(inSrc));
System.out.println("Arrays.toString(temp) = " + Arrays.toString(temp));
System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
}
}
class Try1 {
public static void main(String[] args) {
byte inSrc[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
byte[] outSrc;
byte[] temp = new byte[4];
ByteArrayInputStream input = new ByteArrayInputStream(inSrc);
ByteArrayOutputStream output = new ByteArrayOutputStream();
try {
while (input.available() > 0) {
System.out.println("input.available() = " + input.available());
// available은 blocking없이 읽을수 있는 stream 개수 반환
input.read(temp); // 4개만 읽기 가능 - temp길이가 4개 이기 때문
output.write(temp);
// 4개를 보내주는데 마지막에 2개만 보내줘야하지만
// byte[]는 값을 덮어쓰기 때문에 마지막에 8,9,6,7을 보내게 됨.
outSrc = output.toByteArray();
System.out.println("Arrays.toString(temp) = " + Arrays.toString(temp));
System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
}
} catch (IOException e) {
// read, write는 필수로 IOException 예외처리를 해야함
}
}
}
3.3. FileStream - 자주 사용
- 생성자
// input
FileInputStream(String name) // 지정된 파일이름(name)을 가진 실제 파일과 연결된 FileInputStream 생성
FileInputStream(File file) // 파일이 file 인스턴스인 것을 제외하고 위에 생성자와 동일
FileInputStream(FileDescriptor fdObj) // 파일 디스크립터로 fileInputStream을 생성
// output
FileOutputStream(String name)
// 지정된 파일이름을 가진 실제 파일과 연결된 fileoutputstream 생성
// 지정된 파일없을시 생성, 존재시 덮어쓰기 default
FileOutputStream(String name, boolean append) // append true 일때 덮어쓰지 않고 파일 마지막에 내용 덧붙임
FileOutputStream(File file) // 파일의 이름을 String이 아닌 File 인스턴스로 지정한것 말고 동일
FileOutputStream(File file, boolean append) // 동일
FileOutputStream(FileDescriptor fdObj) // 파일 디스크립터로 fileOutputStream을 생성
- FileStream - read() 예시
- 만약 readfile에 한글 존재시 글자 깨짐 -> byte의 unicode가 다 처리 못함
- read 일때만 그렇고 실제 write한 파일에는 잘 처리됨
public class Try3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("절대경로+Try3.java");
int data = 0;
while ((data = fis.read()) != -1) {
// int read = fis.read(); // byte로 나옴
// System.out.println("read = " + read);
char c = (char) data;
System.out.print(c);
}
}
}
- 중요
- new FileOutputStream("생성하고 싶은 txt 파일명") : 해당 stream에 write하면 byte코드로 해당 instance에 저장해서 실제 txt 파일에 사람이 읽을 수 있는 원래 언어로 변경되는 일련의 과정을 자기가 알아서 다 처리해줌
- 이전 Byte 같은 경우는 write가 다 해주지 않아서 직접 toArray 써가면서 사람이 읽을 수 있게 수동으로 변경한 것
public class Try3 {
public static void main(String[] args){
try {
FileInputStream fis = new FileInputStream("절대경로 + Try3.java");
// Try3.java와 연결된 FileStream 생성 - fileStream 객체 내부에 해당 파일을 byte로 저장한 배열이 존재
System.out.println("fis = " + fis); // fis = java.io.FileInputStream@372f7a8d
FileOutputStream fos = new FileOutputStream("hi.txt");
// fileStream을 통해서 file생성
// write를 통해서 들어온 byte type의 data를 fileStream에 저장
// 실제 출력값을 저장할 hi.txt(아까 위의 byte[] outSrc 같은 역할)에는 fileStream이 자동으로 byte->string으로 변환후 저장해줌
// 위 예제에서는 toByteArray로 직접 작성해줬었다.
System.out.println("fos = " + fos); // fos = java.io.FileOutputStream@2f92e0f4
// byteStream 예외 동일
int data = 0;
while ((data = fis.read()) != -1) {
fos.write(data);
System.out.print((char) data); //read 된 것보고 싶어서 넣음
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 보조 스트림
4.1. FilterStream
- Input Output Stream의 자손
- 모든 보조 스트림의 조상
- 역할: 기반 스트림의 메서드를 그대로 호출해준다.
4.2. BufferedStream
- filter stream의 자손
- 기반 스트림의 입출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림
- 1 byte씩 입출력하는 것보다 버퍼를 이용해서 여러 바이트를 입출력하는 것이 빠르기 때문에 사용
- BufferedInputStream
//생성자
BufferedInputStream(InputStream in, int size)
// in 객체를 입력소스로 하고 지정된 크기만큼의 내부 buffer인스턴스 생성
// 내부 buffer 인스턴스로 read하고 하는 것이 외부데이터와 연결해서 read()하는 것보다 효율적이다.
// read()를 통해서 data를 다 읽은 후 다시 read()하면 자동으로 내부 buffer에 size만큼 다시 data를 저장한다.
BufferedInputStream(InputStream in) // 크기를 default로 8192byte로 가진다.
- BufferedOutputStream
// 생성자
BufferedOutputStream(OutputStream out, int size)
// write로부터 오는 출력이 bufferstream에 먼저 저장되고 가득 차면 모든 내용을 실제 가야되는 outputStream으로 모두 간다.
// outputStream은 이전에 공부한 방식으로 출력값을 저장해야되는 공간으로 이동시킨다.(abc.txt, byte[] abc)
// 마지막 부분은 buffer가 가득차지 않으므로 close()나 flush()로 마지막 버퍼를 다 출력시켜줘야한다.
BufferedOutputStream(OutputStream out) // 크기를 default로 8192byte 가짐
// method
flush() // buffer의 모든 내용을 출력소스(outputStream)으로 출력(이동) 후 버퍼를 비운다.
close() // flush를 호출 한 후, bufferoutputStream 객체서 사용하던 모든 자원을 반환한다.
- 사용 예
- char를 넣어야 하는 이유
- char 타입은 2바이트 크기의 데이터 타입이며, 0 ~ 65535 범위의 유니코드 문자를 나타낸다.
- char 타입으로 1과 9를 사용하면 2바이트 크기의 유니코드 문자가 출력
바이트 스트림으로 변환할 때 인코딩 방식을 지정해줄 수 있으며, 정상 결과 나타난다.
- char 타입으로 1과 9를 사용하면 2바이트 크기의 유니코드 문자가 출력
- int 타입은 4바이트 크기의 데이터 타입이며, -2147483648 ~ 2147483647 범위의 정수를 나타낸다.
- for문에서 int 타입으로 1과 9를 사용하면 1부터 8까지의 4바이트 크기의 정수값이 출력
문자로 변환하면 현재 사용되는 인코딩에 따라 예상치 못한 결과가 나타난다.
- for문에서 int 타입으로 1과 9를 사용하면 1부터 8까지의 4바이트 크기의 정수값이 출력
- char 타입은 2바이트 크기의 데이터 타입이며, 0 ~ 65535 범위의 유니코드 문자를 나타낸다.
- char를 넣어야 하는 이유
public class Try3 {
public static void main(String[] args){
try {
FileOutputStream fos = new FileOutputStream("hi.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
for (int i = '1'; i < '9'; i++) { // 해당 char의 int값을 사용해야 올바른 문자가 출력됨
bos.write(i);
}
fos.close(); // 사용안하면 1,2,3,4,5만 저장됨
bos.close(); // 사용안하면 1,2,3,4,5만 저장됨 - buffer에 담은 것을 다 꺼내야 함
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.3. SequenceStream
- SequenceInputStream
- 여러 개의 입력스트림을 연속적으로 연결해서 하나의 스트림으로부터 데이터를 읽는 것과 같이 처리
- 큰 파일을 여러 개의 작은 파일로 나눠서 작업했다가 마지막에 파일을 합칠 때 좋다.
// 생성자
SequenceInputStream(Enumeration e) // Enumeration에 저장된 순서대로 입력스트림을 하나의 스트림으로 연결
SequenceInputStream(InputStream s1, InputStream s2) // 2개의 입력스트림을 하나의 스트림으로 연결
// 사용법
// 1. Vector에 해당 stream 객체들을 넣은 후 sequenceStream 이용하기 - 합할 stream이 3개 이상일 때 사용
Vector files = new Vector();
files.add(new FileInputStream("file.001"));
files.add(new FileInputStream("file.002"));
System.out.println("v = " + v);
SequenceInputStream in = new SequenceInputStream(files.elements());
// 2. 파일을 보조스트림에 다이렉트로 박아넣음
FileInputStream file1 = new FileInputStream("file.001");
FileInputStream file2 = new FileInputStream("file.002");
SequenceInputStream in = new SequenceInputStream(file1, file2);
- SequenceOutputStream
- stream이 3개 이상일 때는 vector와 sequenceStream을 잘 조합해서 사용 필요
public class Try3 {
public static void main(String[] args){
byte[] arr1 = {1,2,3};
byte[] arr2 = {4,5,6};
byte[] arr3 = {7,8,9};
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) {
e.printStackTrace();
}
outSrc = output.toByteArray();
System.out.println("Arrays.toString(outSrc) = " + Arrays.toString(outSrc));
}
}
4.4. PrintStream
- System.out.xxx <- 여기 들어가던 println(), printf(), print() 요녀석들 얘기하는 것
5. Char 기반 stream
5.1. Reader
- InputStream의 메서드와 다를게 없다.
- byte배열대신 char배열 사용한다는 것만 차이점
- fileInputStream은 한글이 깨져서 출력되지만 Reader는 인코딩과 자바에서 사용하는 유니코드 간의 변환을 자동적으로 처리
// Reader
boolean ready() // 입력소스로부터 데이터를 읽을 준비가 됬는지 알림
long skip(long n) // 스트림에서 주어진 길이만큼 건너뜀
boolean markSupported() //mark와 reset을 지원하는지 확인해줌
void mark(int readlimit) // 현재위치를 표시, reset()으로 해당 위치로 돌아갈 수 잇다. params는 되돌아 갈 수 있는 수
void reset() // 마지막 mark가 호출된 곳으로 되돌아감
int read() // 하나의 문자를 읽어온다. 마지막 데이터에 도달시 -1 반환
int read(char[] c) // 배열 c 길이 먼저 읽고 c배열 길이만큼 data 반환
int read(charBuffer target) // 입력소스로를 읽어서 문자버퍼에 저장
abstract int read(char[] c, int off, int len) // 입력소스로부터 최대 len개 문자읽어서, 배열 c에 지정된 off부터 len 길이만큼 저장
abstract void close() // 스트림을 닫음으로써 사용하고 있던 자원을 반환
5.2. Writer
- OutputStream의 메서드와 다를게 없다.
- byte배열대신 char배열 사용한다는 것만 차이점
- 문자열 인코딩과 자바에서 사용하는 유니코드간의 변환을 자동적으로 처리
// Writer
Writer append(char c) // 지정된 문자를 출력소스에 출력
Writer append(CharSequence c) // 지정된 문자열을 출력소스에 출력
Writer append(CharSequence c, int start, inst end) // 지정된 문자열 일부를 출력소스에 출력
void write(int b) // 주어진 값을 출력소스에 사용
void write(String str) // 주어진 문자열을 출력소스에 사용
abstract void write(String str, inf off, int len) // 주어진 str에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용
void write (char[] c) // 주어진 배열 c에 저장된 모든 내용을 출력소스에 사용
abstract void write(char[] c, inf off, int len) // 주어진 배열 c에 저장된 내용중 off번째부터 len개수만큼 읽어서 출력소스에 사용
abstract void flush()// 스트림의 버퍼에 있는 모든 내용을 출력소스에 사용(buffer가 있는 스트림에 한정)
abstract void close() // 입력소스를 닫음으로써 사용하고 있던 자원을 반환
5.3. 사용 예
- 인코딩 변환 확인 예제
public class Try3 {
public static void main(String[] args){
try {
String fileName = "hi.txt";
FileInputStream fis = new FileInputStream(fileName);
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 (IOException e) {
}
}
}
- 띄어쓰기 공백 tab 제거
public class Try3 {
public static void main(String[] args){
try {
FileReader fr = new FileReader("hi.txt");
FileWriter fw = new FileWriter("bye.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();
}
}
}
5.4. StringReader, StringWriter
- StringWriter에 출력되는 data는 StringWriter 내부의 StringBuffer에 저장된다. - StringBuffer 객체 꺼내고싶으면 getBuffer() 사용하면 된다.
public class Try3 {
public static void main(String[] args){
String inputData = "abcd";
StringReader sr = new StringReader(inputData);
StringWriter sw = new StringWriter();
int data = 0;
try {
while ((data = sr.read()) != -1) {
sw.write(data);
}
StringBuffer buffer = sw.getBuffer();
System.out.println("buffer = " + buffer);
sr.close();
sw.close();
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("inputData = " + inputData);
System.out.println("sw = " + sw);
}
}
5. 보조 스트림
5.1. BufferedReader, BufferedWriter
- 이전 보조 스트림과 동일
- readLine() : 파일을 라인단위로 읽는다. -> String line에 read한 값을 넣음
public class Try3 {
public static void main(String[] args){
try {
FileReader fr = new FileReader("hi.txt");
BufferedReader br = new BufferedReader(fr);
String line = "";
for (int i = 1; (line = br.readLine()) != null; i++) {
// ;를 포함한 라인을 출력
if (line.indexOf(";") != -1) {
System.out.println(i + ":" + line);
}
}
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
6. 기타 스트림
6.1. 연결 Stream - InputStreamReader, OutputStreamWriter
- 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할
- 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업 수행
- 생성자와 메서드
// Reader 생성자, 메서드
InputStreamReader(InputStream in) // os에서 사용하는 기본 인코딩 문자로 변환후 Reader 생성
InputStreamReader(InputStream in, String encoding) // 지정한 인코딩을 사용한 Reader 생성
String getEncoding() // InputStreamReader의 인코딩을 알려준다.
// Writer 생성자, 메서드
OutputStreamWriter(OutputStream in) // os에서 사용하는 기본 인코딩 문자로 변환후 Writer 생성
OutputStreamWriter(OutputStream in, String encoding) // 지정한 인코딩을 사용한 Writer 생성
String getEncoding() // OutputStreamWriter의 인코딩을 알려준다.
6.2. 표준 입출력
- 표준 입출력: 콘솔을 통한 데이터 입력과 콘솔로의 데이터 출력
- System.in
- InputStream으로부터 input값 받음
- 콘솔로부터 데이터를 입력받는데 사용
- System.out
- PrintStream으로 보조stream이용해서 출력값 도스창에 출력
- 콘솔로 데이터를 출력하는데 사용
- System.err
- PrintStream으로 보조stream이용해서 출력값 도스창에 출력
- 콘솔로 데이터를 출력하는데 사용
- System.in
- in, out, err: System class에 선언된 클래스변수(static 변수)
- 표준 입출력 대상변경
- 콘솔 화면에서부터 다른 입출력대상으로 변경 가능
// 입출력 대상변경 method
static void setOut(PrintStream out) // System.out 출력을 지정된 PrintStream으로 변경
static void setErr(PrintStream err) // System.err 출력을 지정된 PrintStream으로 변경
static void setIn(PrintStream in) // System.in 출력을 지정된 InputStream으로 변경
입출력 대상변경 사용예
public class Try3 {
public static void main(String[] args){
PrintStream ps = null;
FileOutputStream fos = null;
try {
fos = new FileOutputStream("test.txt");
ps = new PrintStream(fos);
System.setOut(ps);
} catch (IOException e) {
System.err.println("File not found.");
}
System.out.println("hi");
System.err.println("hi");
}
}
6.3. 직렬화, ObjectnputStream, ObjectOutPutStream
- 직렬화
- 객체를 데이터 스트림으로 만드는 것
- 객체에 저장된 데이터를 스트림에 write() 하기 위해 연속적인 데이터로 변환 하는 것
- 역직렬화
- stream으로부터 data를 읽어서 객체를 만드는 것
- ObjectInputStream, ObjectOutputStream
- 보조스트림 중 하나
- 객체 생성할 때 입출력할 스트림을 먼저 지정해줘야한다.
- ObjectInputStream
- 역직렬화
- ObjectInputStream.readObject();
- ObjectOutputStream
- 직렬화
- ObjectOutputStream.writeObject();
- readXXX(), writeXXX() : Object 타입 이외에도 여러 타입을 가진 메서드를 제공, 어지간한 타입 다 존재함
- 사용
// 직렬화
FileOutputStream fos = new FileOutputStream("파일명.txt"); // 3. 실제 파일에 객체 저장됨
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new 객체()); // 1. 2.
// 1. 객체를 파일명.txt에 저장하는 과정
// 2.보조stream에 객체 직렬화
// 역직렬화
FileInputStream fos = new FileInputStream("파일명.txt"); // 1. 객체가 저장된 실제 파일 inputstream 생성
ObjectInputStream in = new ObjectInputStream(fos);
객체타입 info = (객체타입)in.readObject(); // 2. 3.
// 2. 보조stream에 객체 역직렬화 -> Object type으로 나옴
// 3. 형변환 필요
- 직렬화 가능한 클래스 생성
- 직렬화 하고 싶은 필드를 Serializable interface 구현만 하면 끝
- 조건
- class에 Serializable 구현시 : 모든 필드 직렬화 대상에 포함
- 조상 class에서 Serializable 구현시 : 조상 필드부터 자손 필드까지 직렬화 대상에 포함
- 자손 class에서만 Serializable 구현시 : 조상 필드는 직렬화에 제외
public class Info{
String name;
String password;
int age;
}
// 직렬화 class
public class Info implements Serializable{
String name;
String password;
int age;
}
- 직렬화 제외
- Serializable을 구현해도 필드내에 직렬화가 불가한 필드가 존재시 NotSerializableException 발생한다.
- 직렬화가 제외되는 필드
- 참조변수가 아니라 참조변수가 가리키는 객체가 기본형이 아닌 객체타입일 때 불가
- password같이 보안상 넘기면 안되는 필드
// 직렬화 제외 class
public class Info implements Serializable{
String name;
int age;
transient String password; // 중요한 보안 정보
transient Object obj = new Object(); // 참조변수의 실제타입이 객체인 경우
Object strObj = new String("abc"); // 현재는 예외가 발생하지만 Object class에 Serializable 구현하면 정상 작동
}
- 직렬화
public static void main(String[] args) throws IOException {
try {
String fileName = "test.txt";
FileOutputStream fos = new FileOutputStream(fileName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ObjectOutputStream out = new ObjectOutputStream(bos);
Try3 t1 = new Try3("1", "234", 12);
Try3 t2 = new Try3("2", "23334", 121);
ArrayList<Try3> list = new ArrayList<>();
list.add(t1);
list.add(t2);
out.writeObject(t1);
out.writeObject(t2);
out.writeObject(list);
out.close();
System.out.println("직렬화 잘 끝");
} catch (IOException e) {
e.printStackTrace();
}
}
- 역직렬화
- 객체를 읽을 때 저장한 순서를 지켜야한다.
- 그래서 객체에 직렬화할 때 collection framework를 이용해서 안에 한번에 넣어주면 순서를 고려할 필요가 없으므로 객체 각각 저장하는 것보다 Collection type으로 한번에 직렬화하는 것을 추천
public static void main(String[] args) throws IOException {
try {
String fileName = "abc.txt";
FileInputStream fis = new FileInputStream(fileName);
BufferedInputStream bis = new BufferedInputStream(fis);
ObjectInputStream in = new ObjectInputStream(bis);
// 객체 읽을때는 출력한 순서와 동일한 순서로 읽어야 한다.
Try3 t1 = (Try3) in.readObject();
Try3 t2 = (Try3) in.readObject();
ArrayList list = (ArrayList) in.readObject();
System.out.println(t1);
System.out.println(t2);
System.out.println(list);
in.close();
System.out.println("역직렬화 잘 끝");
} catch (Exception e) {
e.printStackTrace();
}
}
7. File
- 기본적이면서도 가장 많이 사용되는 입출력 대상
- File class를 통해서 파일과 디렉토리를 다룰 수 있도록 한다.
- File 인스턴스는 file일수도 있고 directory가 될 수도 있다.
- 생성자, 메서드
// 생성자
// fileName을 이름으로 갖는 file을 위한 File instance 생성
// directory도 동일한 방법으로 생성
// fileName은 주로 path를 포함해서 지정해준다.
// path 지정 안할 시, default 위치에 생성
File(String fileName) // 위에 작성
File(String pathName, String fileName) // path와 파일명 분리해서 지정
File(File pathName, String fileName) // File 인스턴스로 된 path와 파일명 분리해서 지정
File(URI uri) // 지정된 uri로 파일 생성
// method
String getName() // 파일이름 String으로 반환
String getPath() // 파일경로 String으로 반환
String getAbsolutePath() // 파일절대경로 String으로 반환
File getAbsoluteFile() // 파일절대경로 File으로 반환
String getParent() // 파일 조상 directory String으로 반환
File getParentFile() // 파일 조상 directory File로 반환
String getCanonicalPath() // 파일의 정규경로를 String으로 반환
File getCanonicalFile() // 파일의 정규경로를 File로 반환
// 멤버변수
static String pathSeparator // os에서 사용하는 경로 구분자
static char pathSeparator // os에서 사용하는 경로 구분자
static String separator // os에서 사용하는 이름 구분자
static char separator // os에서 사용하는 이름 구분자
- 예제에 나온 추가 method
- System.getProperty("user.dir")); // 현재 프로그램이 실행중인 directory
- createNewFile(); // 새 파일 생성
- mkdir(); // 새 디렉토리 생성
- listFiles(); // 해당 dir 내부 dir, 파일들 File[]로 추출하기
- delete(File, 삭제조건); // 삭제
- renameTo(File f, 변경할 파일명); // 파일 이름 변경
// file 생성자 사용방법
// 기존에 존재하는 파일을 참조만 하는 것
// 파일 생성하지 않음
File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice","Try3.java");
File dir = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice");
File f = new File(dir, "Try3.java");
// 새로운 파일 생성
File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice", "NewFile.java");
f.createNewFile(); // 새 file 생성
// 새로운 directory 생성
File dir = new File("/Users/유저명/Documents/javaSpring/untitled/src","newDirectory");
dir.mkdir(); // 새 directory 생성
- 예제1
public class Try3 {
public static void main(String[] args) throws IOException {
File f = new File("/Users/유저명/Documents/javaSpring/untitled/src/practice/Try3.java");
String fileName = f.getName();
int pos = fileName.lastIndexOf(".");
System.out.println("f.getName() = " + f.getName());
System.out.println("fileName.substring(0,pos) = " + fileName.substring(0, pos));
System.out.println("fileName.substring(pos+1) = " + fileName.substring(pos + 1));
System.out.println("f.getPath() = " + f.getPath());
System.out.println("f.getAbsolutePath() = " + f.getAbsolutePath());
System.out.println("f.getCanonicalPath() = " + f.getCanonicalPath());
System.out.println("f.getParent() = " + f.getParent());
System.out.println("System.getProperty(\"user.dir\") = " + System.getProperty("user.dir")); // 현재 프로그램이 실행중인 directory
System.out.println("File.pathSeparator = " + File.pathSeparator);
System.out.println("File.pathSeparatorChar = " + File.pathSeparatorChar);
System.out.println("File.separator = " + File.separator);
System.out.println("File.separatorChar = " + File.separatorChar);
}
}
- 예제2 - 파일과 디렉토리 목록 보여주기
public class Try3 {
public static void main(String[] args) throws IOException {
if (args.length != 1) {
System.out.println("usage : my package");
System.exit(0);
}
File f = new File(args[0]);
if (!f.exists() || !f.isDirectory()) {
System.out.println("유효하지 않은 디렉토리");
System.exit(0);
}
File[] files = f.listFiles();
for (int i = 0; i < files.length; i++) {
String fileName = files[i].getName();
System.out.println(files[i].isDirectory() ? "[" + fileName + "]" : fileName);
}
}
}
이전 발행글 : 2023.03.24 - [java/java 기초] - 14. 람다와 스트림