입출력(I/O)

주8·2023년 2월 1일
0
  • I/O란 입력(Input)과 출력(Output)의 줄임말로 두 대상간의 데이터를 주고 받는 것을 의미한다.
  • Java는 스트림(Stream)의 개념을 사용하여 I/O 작업을 빠르게 만든다.
  • java.io 패키지에는 입력 및 출력 작업에 필요한 모든 클래스와 인터페이스가 포함되어 있다.
    • 파일, 네트워크, 스레드간 통신(Pipe), 버퍼링, 필터링, 파싱(Parsing), 테스트 데이터, 기본형 데이터, 객체
  • java I/O API를 사용하여 Java에서 파일 처리를 수행할 수 있다.
  • Stream: 스트림은 데이터의 시퀀스이다. Java에서 스트임은 바이트로 구성된다.
  • Java에서는 3개의 스트림이 자동으로 생성되며 이 모든 스트림은 콘솔에 연결된다.
    • System.out: 표준 출력 스트림
    • System.in: 표준 입력 스트림
    • System.err: 표준 오류 스트림

I/O 클래스 구성

  • 입력, 출력, 바이트 기반, 문자 기반 및 버퍼링, 구문 분석 등과 같이 구체적인 목적으로 구분된 Java IO 클래스의 표
구분Byte 기반Character 기반
InputOutputInputOutput
BasicInputStreamOutputStreamReader
InputStreamReader
Writer
OutputStreamReader
ArraysByteArrayInputStreamByteArrayOutputStreamCharArrayReaderCharArrayWriter
FilesFileInputStream
RandomAccessFile
FileOutputStream
RandomAccessFile
FileReaderFileWriter
PipesPipedInputStreamPipedOutputStreamPipedReaderPipiedWriter
BufferingBufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter
FilteringFilterInputStreamFilterOutputStreamFilterReaderFilterWriter
ParsingPushbackInputStream
StreamTokenizerPushbackReader
LineNumberReader
StringsStringReaderStringWriter
DataDataInputStreamDataOutputStream
Data-FormattedPrintStreamPrintWriter
ObjectsObjectInputStreamObjectOutputStream
UtilitiesSequenceInputStream

Stream

  • 데이터를 운반하는데 사용되는 연결통로(데이터가 흘러갈 수 있는 길을 만들어주는 역할)
  • 연속적인 데이터의 흐름을 물에 비유해서 붙여진 이름
  • 입력스트림(Input Stream)과 출력스트림(Output Stream) 2개의 스트림으로 나뉜다.
  • 자바에서 사용되는 모든 I/O 클래스들은 스트림 방식으로 데이터를 Read/Write 한다.
  • 스트림은 데이터가 있는 소스(Source)와 데이터가 전달되어지는 목적지(Destination)로 데이터를 연속적으로 흘려보낸다.
  • Input/Output source를 갖는 순서화된 일련의 자료를 나타내는 추상적 입출력 모델이다.
  • 대부분의 자바 I/O 관련 메소드는 IOException을 throw하도록 되어 있다.
  • 기반 스트림: 대상에 직접 자료를 Read/Write하는 기능의 스트림
  • 보조 스트림: 직접 Read/Write하는 기능은 없으며, 추가적인 기능을 제공해 주는 스트림으로 기반 스트림이나 다른 보조 스트림을 생성자의 매개변수로 포함한다.
  • 파일 경로: 윈도우는 \, Mac은 / 사용
  • 예) File.separator를 사용하면 OS를 신경쓰지 않아도 된다.
    • 윈도우 - C:\Users\jongkwonkim\Documents\streamtest\testme.txt
    • Mac(리눅스) - /Users/jongkwonkim/Documents/streamtest/testme.txt
    • File.separator 사용 :
      String separator = File.separator;
      File targetFile = new File(separator + "Users" + separator + "jongkwonkim" + separator + "Documents" + separator + “streamtest");

  • Stream은 byte 단위 처리와 Character 단위의 Stream으로 나뉜다.
    • Byte Stream은 1 Byte 바이너리 데이터(파일, 이미지로딩 등) 처리를 위해 사용
    • Character Stream은 주로 2 Byte 문자데이터(Char 단위) 처리를 위해 사용
    • InputStream 또는 Reader는 데이터 소스에 연결되며, OutputStream 또는 Writer는 데이터 destination에 연결된다.
StreamByte StreamCharacter Stream
Source streams (입력 데이터의 시작)InputStreamReader
Sink streams (데이터가 처리되어 출력되는 끝)OutputStreamWriter

Byte Stream

  • 바이트 단위로 데이터를 주고 받으며 입출력 대상에 맞는 Stream 클래스가 지원된다.
  • InputStream 또는 OutputStream의 하위클래스들이다.

입력스트림출력스트림대상
FileInputStreamFileOutputStream파일
ByteArrayInputStreamByteArrayOutputStream메모리
PipedInputStreamPipedOutputStream프로세스
AudioInputStreamAudioOutputStream오디오장치

InputStreamOutputStream
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설명
int read()입력 스트림에서 데이터의 다음 바이트를 read한다. 파일 끝일 경우 -1을 반환한다.
int read(byte[] b)byte[] b만큼의 데이터를 읽어서 b에 저장하고 읽은 바이트 수를 반환한다.
int available()현재 입력스트림에서 읽을 수 있는 바이트 수의 추정치를 반환한다.
mark()데이터가 읽혀진 InputStream의 위치를 표시한다.
void close()현재 입력 스트림을 close 하는데 사용된다.

OutputStream설명
void write(int b)현재 출력 스트림에 바이트를 write하는데 사용된다.
void write(byte[] b)현재 출력 스트림에 바이트 배열을 write하는데 사용된다.
void flush()현재 출력 스트림을 flush한다.
void close()현재 출력 스트림을 닫는데 사용된다.

  • InputStream과 OutputStream은 추상클래스이며, 하위 클래스들이 각 특성에 맞게 정의한다.
    • Byte stream read()의 반환타입이 int인 이유는 8 bit I/O를 수행하고 반환값이 0~255(영문 대,소문자 등의 ASCII 값)와 -1이기 때문이다. 즉 byte형으로 반환할 경우 파일의 끝을 나타내는 기호로 사용할 마땅한 값이 없기 때문이다.
    • 리턴타입이 int형이어서 1byte(8bit)인 바이너리코드를 int형으로 변환하여 숫자값을 리턴
  • Stream의 사용이 종료되면 반드시 Stream의 close() 메서드를 호출해서 리소스 누수를 막아야 한다.

  • InputStream의 read()와 OutputStream의 write() 메서드를 이용한 파일 Copy
public class FileCopyByteEx1 {
	public static void main(String[] args) throws IOException {
		FileInputStream in = null; FileOutputStream out = null; 
		try {
			in = new FileInputStream( "/Users/jongkwonkim/Documents/streamtest/testme.txt");
			out = new FileOutputStream( "/Users/jongkwonkim/Documents/streamtest/copy_testme.txt");
			int c;
			while ((c = in.read()) != -1) {
				out.write(c); 
			}
		} finally {
			if (in != null) {
				in.close(); 
			}
			if (out != null) { 
				out.close();
			} 
		}
	} 
}

Character Stream

  • 자바에서 2byte 단위 Unicode를 처리하기 때문에 바이트스트림으로 2Byte인 문자를 처리하는데 어려움이 있어 보완하기 위해 제공(1.1부터)되며, 활용방법은 거의 비슷하다. (한글의 경우 Unicode 2 byte로 처리되므로 한글 처리를 하려면 Character stream을 사용해야 한다.)
  • InputStream > Reader, OutputStream > Writer로 사용
바이트기반 스트림문자기반 스트림대상
FileInputStream
FileOutputStream
FileReader
FileWriter
파일
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
메모리
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
프로세스
StringBufferedInputStream
StringBufferedOutputStream
StringReader
StringWriter
메모리

  • Abstract method가 Input/OutputStream과 다르다.
InputStreamReader
abstract int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
int read()
int read(char[] char)
abstract int read(char[] cbuf, int off, int len)

OutputStreamWriter
abstract void write(int b)
void write(byte[] b)
void write(byte[] b, int off, int len)
void write(int c)
void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)

Processing(chaining) Stream

  • Precess stream은 IO에서 Chaining(연쇄화)를 통해 데이터 타입별로 스트림의 기능을 향상시키거나 새로운 기능을 추가할 수 있다. (Decorator Pattern)
  • 각 class는 정해진 일을 하고 연결된 class에 forward한다.
  • 예) text.txt 파일의 읽기를 향상시키기 위해 FileInputStream에 BufferedInputStream을 chaining한다.

예제

//기반 스트림을 생성
FileInputStream fin = new FileInputStream("test.txt");
//기반 스트림을 이용한 보조 스트림 생성
BufferedInputStream bin = new BufferedInputStream(fin);
...
//보조 스트림인 BufferedInputStream으로 데이터를 읽는다
bin.read();

입력출력설명
FileInputStreamFileOutputStream필터를 이용한 입출력 처리
BufferedInputStreamBufferedOutputStream버퍼를 이용한 입출력 성능향상
DataInputStreamDataOutputStreamint, float과 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능
SequenceInputStreamSequenceOutputStream두 개의 스트림을 하나로 연결
ObjectInputStreamObjectOutputStream데이터를 객체단위로 read/write하는데 사용한다.

Byte Stream

  • InputStream과 OutputStream은 모든 Byte Stream의 추상 슈퍼클래스이다. InputStream 메서드는 아래와 같다.
메서드명설명
int available()스트림으로부터 read 할 수 있는 데이터의 크기를 반환
void close()스트림을 close로써 사용하고 있던 자원 반환
void mark(int readlimit)현재위치를 표시한다. 필요시에 reset()에 의해서 표시해놓은 위치로 되돌아갈 수 있다. readlimit은 되돌아갈 수 있는 byte의 수이다.
boolean markSupported()mark()와 reset()을 지원하는지를 알려준다. mark()와 reset()을 사용하기 전에 markSupported()를 호출해서 지원여부를 확인해야 한다.
abstract int read()1byte를 read한다(0~255 사이 값). read할 데이터가 없으면 -1을 반환한다.
abstract 메서드로 InputStream을 상속하는 클래스들의 특성에 맞게 구현한다.
int read(byte[] b)byte 배열 b의 사이즈만큼 read한 후 데이터의 수를 반환하며 반환 값은 배열의 크기보다 작거나 같다.
int read(byte[] b, int off, int len)최대 len개의 byte를 read해서, 배열 b의 off부터 저장하며, read할 수 있는 데이터가 len보다 적을 수 있다.
void reset()스트림에서 위치를 마지막으로 mark() 메서드가 호출된 위치로 되돌아간다.
long skip(long n)스트림에서 길이(n) 만큼 skip한다.

  • OutputStream 메서드
메서드명설명
void close()사용하던 자원을 반환한다.
void flush()스트림의 버퍼에 있는 모든 것을 출력소스에 write한다.
abstract void write(int b)int b 값을 출력소스에 write한다.
void write(byte[] b)byte 배열 b를 출력소스에 write한다.
void write(byte[] b, int off, int len)byte 배열 b에 저장된 내용 중에서 off 위치부터 len 만큼 read해서 출력소스에 write한다.

  • InputStream과 OutputStream 메소드 사용시 주의사항
    • mark()와 reset()을 사용하여 이미 읽은 데이터를 되돌려서 다시 읽을 수 있으나 이 기능을 지원하는 클래스인지 markSupported()를 통해서 알 수 있다.
    • flush()는 버퍼를 비우는 OutputStream의 기능이며, Stream close 전에 비우도록 한다.
    • Stream을 사용하고 나서는 close()를 호출해서 반드시 닫아주어야 한다. (JVM이 자동적으로 닫아주기는 한다.)

ByteArrayInputStream, ByteArrayOutputStream

  • 메모리에 바이트배열을 입/출력할 때 사용하는 스트림이다. (GC에 자원반납이 되므로 close()는 무의미하다)
  • 주로 다른 곳에 입출력하기 전에 임시로 담아서 변환 등의 작업시 사용한다.
byte[] buf = {35,36,37,38};
//새로운 바이트 배열 입력 스트림 생성
ByteArrayInputStream byt = new ByteArrayInputStream(buf);
int k = 0;
while((k = byt.read()) != -1){
	//byte 값을 char 문자로 변환
	char ch = (char) k;
	System.out.println("문자의 ASCII 값: " + k + "; 특수문자: " + ch);
}

[결과]
문자의 ASCII 값: 35; 특수문자: #
문자의 ASCII 값: 36; 특수문자: $
문자의 ASCII 값: 37; 특수문자: %
문자의 ASCII 값: 38; 특수문자: &
FileOutputStream fout =
	new FileOutputStream("/Users/jongkwonkim/Documents/streamtest/fout.txt");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
bout.write(68);
bout.writeTo(fout);

bout.flush();
bout.close();

[결과]
/Users/jongkwonkim/Documents/streamtest/fout.txt 파일이 생성됨
파일을 열어보면 char타입 문자 'D'가 저장되어 있다

FileInputStream, FileOutputStream

  • 파일 입출력을 위한 스트림이다.
메서드명설명
FileInputStream(String name)지정 파일이름의 실제 파일과 연결된 FileInputStream을 생성한다.
FileInputStream(File file)지정한 File 인스턴스로 FileInputStream을 생성한다.
FileOutputStream(String name)지정 파일이름의 실제 파일과 연결된 FileOutputStream을 생성한다.
FileOutputStream(String name, boolean append)지정 파일이름의 실제 파일과 연결된 FileOutputStream을 생성함에 있다.
두번째 인자인 append를 true로 하면 파일의 마지막 내용에 덧붙이며, false면 덮어쓰게 된다.
FileOutputStream(File file)지정한 File 인스턴스로 FileOutputStream을 생성한다.

  • 파일 입출력을 위한 스트림이다.
class FileViewer{
	public static void main(String[] args) throws IOException{
		FileInputStream fis = new FileInputStream(args[0]);
		int data = 0;
		while((data=fis.read())!=-1){ 
			//read return type이 int인 것은 
			//1byte씩 파일로부터 데이터를 읽어 들이지만 더 이상 값이 없음을 알리는 -1을 제외하고는
			//0~255범위의 정수값 int가 가장 효율적이고 빠르다
			char c = (char)data;
			System.out.println(c);
		}
	}
}

  • 텍스트 파일을 처리하는 경우에는 문자기반 스트림인 FileReader/Writer를 사용하는 것이 좋다.
  • 예제(파일 스트림을 열어서 read하여 OutputStream 파일에 write하여 내용을 copy한다.)
try{
	FileInputStream fin =
	new FileInputStream("/testme.txt");
	FileOutputStream fos =
	new FileOutputStream("/copytestme.txt");
	int data = 0;
	while((data=fin.read())!=-1){
		fos.write(data):
	}
	fin.close();
	fos.close();
}catch(Exception e){
	e.printStackTrace();
}

FilterInputStream, FilterOutputStream

  • 모든 바이트기반 보조스트림(Chaining Stream)의 최상위 클래스
  • Chaining Stream은 자체 입출력 기능이 없기 때문에 기반 스트림이 필요하다.
  • 상속을 통해 FilterInputStream, FilterOutputStream의 read(), write()를 기능에 맞게 오버라이딩해야 한다.
  • 상속관계
    • java.io.FilterInputStream
      • java.io.BufferedInputStream
      • java.io.DataInputStream (implements java.io.DataInput)
      • java.io.LineNumberInputStream
      • java.io.PushbackInputStream
    • java.io.FilterOutputStream
      • java.io.BufferedOutputStream
      • java.io.DataOutputStream (implements java.io.DataOutput)
      • java.io.PrintStream (implements java.lang.Appendable, java.io.Closeable)

BufferedInputStream, BufferedOutputStream

  • 스트림의 입출력 효율을 높이기 위해 메모리 버퍼를 사용하는 chaining stream 클래스이다.
  • 1byte씩 입출력하는 것보다 바이트배열(버퍼)를 이요해서 한번에 입출력하는 것이 빠르기 때문이다.
  • 버퍼를 사용하지 않게되면, 입/출력시 OS와 I/O call을 직접적으로 하기 때문에 비용이 많이 드는 요청이다. 버퍼를 사용하게 되면 버퍼 메모리에 쌓아두고 읽어들이기 때문에 OS I/O를 콜하는 횟수가 줄어서 Overhead를 줄일 수 있다.
  • 버퍼의 크기는 1024, 2048, 4096이 보통이다.
  • read() 호출 → 입력소스로부터 버퍼 크기만큼 읽어서 자신의 내부버퍼에 저장 → 버퍼에 저장된 데이터를 읽어서 처리 ⇒ 내부의 버퍼로부터 읽기 때문에 작업 효율이 높아진다.
  • write() 메소드의 경우 소스로부터 데이터를 읽어들여 버퍼가 차면, 버퍼의 모든 내용을 출렷소스에 write한다 ⇒ 다시 버퍼를 비우고 출력을 저장할 준비를 한다.
  • 보조스트림을 close하면 기반스트림도 close된다.

  • 메서드와 생성자
메서드명/생성자설명
BufferedInputStream(InputStream in, int size)InputStream 인스턴스를 입력소스로 지정된 크기(byte 단위)의 버퍼를 갖는 BufferedInputStream 인스턴스를 생성한다.
BufferedInputStream(InputStream in)InputStream 인스턴스를 입력소스로 버퍼의 크기를 지정해주지 않으면 Default buffer로 설정된 8,192byte 크기의 버퍼를 가지게 된다.
BufferedOutputStream(OutputStream out, int size)OutputStream 인스턴스를 출력소스로 지정된 크기(byte 단위)의 버퍼를 갖는 BufferedOutputStream 인스턴스를 생성한다.
BufferedOutputStream(OutputStream out)OutputStream 인스턴스를 출력소스로 버퍼의 크기를 지정해주지 않으면 Default buffer로 설정된 8,192byte 크기의 버퍼를 가지게 된다.
flush()버퍼의 모든 내용을 출렷소스에 출력한 후 버퍼를 비운다.
close()flush()를 호출해서 버퍼의 모든 내용을 출력소스에 출력하고, BufferedOutputStream 인스턴스가 사용하던 모든 자원을 반환한다.
public class BufferedInputStream extends FilterInputStream{
	private static int DEFAULT_BUFFER_SIZE = 8192;
}

public class BufferedOutputStream extends FilterOutputStream{
	public BufferedOutputStream(OutputStream out){
		this(out, 8192);
	}
}

  • 버퍼가 남은채로 종료될 수 있기 때문에 flush()나 close()를 실행해서 버퍼의 마지막 내용까지 write 되도록 한다.
  • 버퍼가 가득차면 버퍼의 모든 데이터가 OutputStream으로 flush(push) 된다.
  • 일시적으로 버퍼에 기록되는 데이터는 Disk의 지속적인 write를 최소화하여 프로그램 성능을 향상시킨다.
try{
	FileOutputStream fos =
	new FileOutputStream("/bufFile.txt");
	BufferedOutputStream bos = new BufferedOutputStream(fos);
	String s = "Welcome to stream!!!";
	byte[] b = s.getBytes();
	bos.write(b);
	bos.flush();
	bos.close();
	fos.close();
	System.out.println("Success!");
}catch(IOException e){
	e.printStackTrace();
}

[결과]
Success!

DataInputStream, DataOutputStream

  • 데이터를 read/write할 때 byte단위가 아닌, 8가지 Primitive type의 단위로 read/write 할 수 있는 장점이 있다.
  • DataOutputStream이 출력하는 형식은 각 기본 자료형 값을 16진수로 표현하여 저장한다.(예: int값을 출력하면, 4byte의 16진수로 출력)
  • 각 자료형의 크기가 다르므로 출력할 때와 입력할 때 순서에 주의한다.

  • DataInputStream
메서드명/생성자설명
DataInputStream(read in)지정한 InputStream 인스턴스를 기반스크림으로 하는 DataInputStream 인스턴스를 생성한다.
boolean readBoolean()
byte readByte()
char readChar()
short readShort()
int readInt()
long readLong()
float readFloat()
double readDouble()
각 자료형에 맞는 값을 read한다. 더 이상 read할 값이 없으면 EOFException을 발생시킨다.
String readUTF()UTF-8형식으로 write된 문자열을 read하는 데 사용된다.
int skipBytes(int n)현재 read하고 있는 위치에서 지정된 n 만큼 skip한다.

  • DataOutputStream
메서드명/생성자설명
DataOutputStream(OutputStream out)지정한 OutputStream 인스턴스를 기반스트림으로 하는 DataOutputStream 인스턴스를 생성한다.
void writeBoolean(boolean b)
void writeByte(int b)
void writeChar(int c)
void writeShort(int s)
void writeInt(int i)
void writeLong(long l)
void writeFloat(float f)
void writeDouble(double d)
각 자료형에 맞는 값을 write한다.
void writeUTF(String s)UTF 형식으로 문자를 출력한다.
void writeChars(String s)지정한 문자열을 write한다. writeChar(char c) 메서드를 여러번 호출한 결과와 같다.
int size()지금까지 DataOutputStream에 write된 byte 수를 알려준다.

  • 예제
public class DataOutputStreamEx2 {
	public static void main(String[] args) throws IOException {

		String filename="data.txt";
		//노드 연결(노드 스트림 연결)
		FileOutputStream fos = new FileOutputStream(filename); 
		//필터 스트림과 노드 스트림을 연결
		DataOutputStream dos = new DataOutputStream(fos);

		byte b=65;
		byte[] buf={66,67,68}; 
		short s=100;
		char ch='가';
		boolean bool=true; 
		double d=123.45; 
		String str="홍길동";

		dos.write(b); 
		dos.write(buf,0,buf.length); 
		dos.writeShort(s); 
		dos.writeChar(ch); 
		dos.writeBoolean(bool); 
		dos.writeDouble(d); 
		dos.writeUTF(str);

		System.out.println(dos.size()+"bytes 기록"); 
		// 스트림에 남아있는 데이터가 있으면 flush 
		dos.flush();
		dos.close();
	}
}

👇

public class DataInputStreamEx2 {
	public static void main(String[] args) throws IOException {

		DataInputStream dis = new DataInputStream( 
						new FileInputStream("data.txt"));
		System.out.println(dis.available()+"바이트 읽어들이기"); 
		// 파일에 쓴 순서대로 읽어야 함
		byte a=dis.readByte();
		System.out.println(a);

		byte[]ba=new byte[3]; 
		int n=dis.read(ba); 
		for(byte b:ba){
			System.out.println(b); 
		}
		short s=dis.readShort(); 
		System.out.println(s); 
		System.out.println(dis.readChar()); 
		System.out.println(dis.readBoolean()); 
		System.out.println(dis.readDouble()); 
		System.out.println(dis.readUTF());

		dis.close();
	}
}

[결과]
28바이트 읽어들이기 65
66
67
68
100true
123.45
홍길동

SequenceInputStream

  • 여러 입력스트림을 연속적으로 연결해서 하나의 스트림처럼 다룰 수 있게 해준다.
  • 큰 파일을 여러 개의 작은 파일로 나누었다가 하나의 파일로 합치는 것과 같은 작업에 사용하면 좋다.
  • FilterInputStream 클래스가 아닌 InputStream을 상속받아 구현했다.
메서드명/생성자설명
SequenceInputStream(Enumeration e)Enumeration에 저장된 순서대로 입력스트림을 하나의 스트림으로 연결한다.
SequenceInputStream(InputStream s1, InputStream s2)두 개의 입력스트림을 하나로 연결한다.

  • 예제
FileInputStream input1=
	new FileInputStream("/testme.txt");
/*
* testme.txt 파일 내용
* Welcome!
* test file
* Goodbye~
*/
FileInputStream input2=
	new FileInputStream("/bufFile.txt");
/*
* bufFile.txt 파일 내용
* Welcome to stream!!!
*/
SequenceInputStream inst=new SequenceInputStream(input1, input2);
int j;
while((j=inst.read())!=-1){
	System.out.print((char)j);
}
inst.close();
input1.close();
input2.close();

[결과]
Welcome!
test file
Goodbye~Welcome to stream!!!

PrintStream

  • 데이터를 다양한 형식의 문자로 출력하는 기능을 제공하는 Chaining Stream이다.
  • 문자기반 스트림의 역할을 수행한다. JDK 1.1부터 향상된 문자기반 스트림인 PrintWriter가 추가됨
  • PrintStream보다 PrintWriter(다양한 언어의 문자를 처리하는데 적합)를 사용할 것을 권장한다.
  • printf()는 JDK 1.5부터 추가되었으며, c언어와 같이 Format 출력을 지원한다.
format설명결과
%d10진수65
%o8진수(octal integer)101
%x16진수(hexadecimal integer)41
%c문자A
%s문자열65
%5d5자리 숫자, 빈자리는 공백으로 채운다65
%05d5자리 숫자, 빈자리는 0으로 채운다65
\t탭(tab)
\n줄바꿈 문자(new line)

Reader, Writer

  • 문자기반 스트림 추상클래스이며, byte배열 대신 char배열을 사용한다.
  • 메소드 형식은 Byte Stream과 다르지 않으며, 여러 종류의 인코딩을 지원해준다.
  • 문자기반 입출력 스트림의 최상위 클래스다.

  • Reader 메서드
메서드명설명
abstract void close()입력스트림을 close하면서 사용하던 자원을 반환한다.
void mark(int readlimit)현재 위치를 표시해놓는다. 나중에 reset()에 의해서 표시해 놓은 위치로 다시 되돌아갈 수 있다.
boolean markSupported()mark()와 reset() 지원여부를 확인해준다.
int read()입력소스로부터 하나의 문자를 read한다. char의 범위인 0~65535범위의 정수를 반환하며, 입력스트림의 마지막 데이터에 도달하면 -1을 반환한다.
int read(char[] c)입력소스로부터 매개변수로 주어진 배열 c의 크기만큼 읽어서 배열 c에 저장한다. read해 온 데이터의 개수 또는 -1을 반환한다.
boolean ready()입력소스로부터 데이터를 읽을 준비가 되어있는지 확인한다.
void reset()입력소스에서의 위치를 마지막으로 mark()가 호출되었던 위치로 되돌아간다.
void skip(long n)현재 위치에서 전달된 n 만큼을 skip한다.

  • Writer 메서드
메서드명설명
abstract void close()출력스트림을 close하면서 사용하던 자원을 반환한다.
abstract void flush()스트림의 버퍼에 있는 모든 내용을 출력소스에 write한다.
void write(int b)전달된 값을 출력소스에 write한다.
void write(char[] c)전달된 배열 c에 저장된 모든 내용을 출력소스에 write한다.
void write(String str)전달된 문자열 str을 출력소스에 write한다.
void write(String str, int off, int len)전달된 문자열 str을 off번째 문자부터 len개 만큼의 문자열을 출력소스에 write한다.

FileReader, FileWriter

  • 문자기반의 파일 입출력. 텍스트 파일의 입출력에 사용한다.
public class FileReaderEx01 {
	public static void main(String argsp[]){} }
		try {
			String fileName = "/Users/jongkwonkim/Documents/streamtest/bufFile.txt"; 
			FileReader fr = new FileReader(fileName);

			int data = 0; 
			while((data=fr.read()) != -1){
				System.out.print((char)data); 
			}
			System.out.println();
			fr.close(); 
		}catch(IOException e){
			e.printStackTrace();
		}
	}
}

PipedReader, PipedWriter

  • 프로세스(쓰레드)간의 통신(데이터를 주고 받음)에 사용한다.
  • 입력과 출력 스트림을 하나의 스트림으로 연결해서 데이터를 주고 받는 게 특징이다.
  • 두 쓰레드가 시작하기 전에 PipedReader와 PipedWriter를 연결해야 한다.

예제

try{
	final PipedReader read = new PipedReader();
	final PipedWriter write = new PipedWriter(read);

	Thread readerThread = new Thread(new Runnable(){
		public void run(){
			try{
				int data = read.read();
				while(date != -1){
					System.out.println((char) data);
					data = read.read();
				}
			}catch(Exception ex){}
		}
	});

	Thread writerThread = new Thread(new Runnable(){
		public void run(){
			try{
				write.write("writerThread write...!\n".toCharArray());
			}catch(Exception ex){}
		}
	});

	readerThread.start();
	writerThread.start();
}catch(Exception e){
	e.printStackTrace();
}

[결과]
writerThread write...!

StringReader와 StringWriter

  • CharArrayReader, CharArrayWriter처럼 메모리의 입출력에 사용한다.
  • StringWriter에 출력되는 데이터는 내부의 StringBuffer에 저장된다. (char 배열이다)
String inputData = "String message!";
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());

[결과]
Input Data: String message!
Output Data: String message!

BufferedReader와 BufferedWriter

  • 입출력 효율을 높이기 위해 버퍼(char[])를 사용하는 chaining stream이다.
  • readLine()을 사용하여 라인(line)단위로 읽어올 수 있는 장점이 있고 문자열 검색기능도 장점이다.
  • BufferedReader method: String readLine() 한 라인을 읽어온다.
  • BufferedWriter method: void newLine() 개행문자를 출력한다.
public class BufferedReaderEx01 {
	public static void main(String[] args) {
		try {
			FileReader fr = new FileReader("/Users/jongkwonkim/Documents/streamtest/brtestfile.txt"); 
			BufferedReader br = new BufferedReader(fr);

			String line = "";
			for(int i=1;(line = br.readLine())!=null;i++) {
				System.out.println(i+":"+line); 
			}
			br.close();
		} catch(IOException e) {
			e.printStackTrace(); 
		}
	} // main 
}

[결과]
1:안녕하세요!
2:테스트 파일입니다~

Character Chaining Stream

  • InputStreamReader와 OutputStreamReader
  • 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다.
  • 지정된 인코딩의 문자데이터로 변환하는 작업을 수행한다. (기본은 OS에서 사용하는 인코딩)

  • InputStreamReader 메서드/생성자
메서드명/생성자설명
InputStreamReader(InputStream in)OS의 기본 인코딩의 문자로 변환하는 InputStreamReader를 생성한다.
InputStreamReader(InputStream in, String encoding)지정 인코딩을 사용하는 InputStreamReader를 생성한다.
String getEncoding()InputStreamReader의 인코딩을 알려준다.

  • OutputStreamReader 메서드/생성자
메서드명/생성자설명
OutputStreamReader(OutputStream in)OS의 기본 인코딩의 문자로 변환하는 OutputStreamReader를 생성한다.
OutputStreamReader(OutputStream in, String encoding)지정 인코딩을 사용하는 OutputStreamReader를 생성한다.
String getEncoding()OutputStreamReader의 인코딩을 알려준다.

  • 시스템의 인코딩 정보는 sun.jnu.encoding의 값을 보면 알 수 있다.
  • Stream생성시 인코딩을 저장할 수 있다.
FileInputStream fis = new FileInputStream("test.txt");
InputStreamReader isr = new InputStreamReader(fis, "KSC5601");

RandomAccessFile

  • 하나의 스트림으로 파일에 입력과 출력을 모두 수행할 수 있는 스트림
  • 다른 스트림들과 달리 Object의 하위 클래스이며, DataInput 인터페이스와 DataOutput 인터페이스를 모두 구현했기 때문에 read/write가 모두 가능하다. (기본 자료형 단위로 입출력을 처리할 수 있다)
  • 가장 큰 장점은 파일의 어느 위치에나 입출력이 가능하며 내부적으로 파일포인터를 사용하는데 입출력시에 작업이 수행되는 곳이 바로 파일 포인터가 위치한 곳이다.
메서드명/생성자설명
RandomAccessFile(File file, String name)
RandomAccessFile(String fileName, String mode)
주어진 file에 읽기 또는 읽기와 쓰기를 하기 위한 RandomAccessFile 인스턴스를 생성한다. mode는 “r(읽기)”과 “rw(읽기/쓰기)” 두 가지 값이 지정 가능하다.
long getFilePointer()파일 포인터의 위치를 알려준다.
long length()파일의 크기를 byte 단위로 얻을 수 있다.
void seek(long pos)파일 포인터의 위치를 변경한다. 위치는 파일의 첫 부분부터 pos 크기만큼이다.
void setLength(long newLength)파일 크기를 byte단위로 지정된 길이로 변경한다.
int skipBytes(int n)지정 수 만큼의 byte를 skip한다.

예제와 파일 내용

static final String FILEPATH = "/testme.txt";
public static void main(String[] args){
	try{
		System.out.println(new String(readFromFile(FILEPATH, 0, 13)));
		writeToFile(FILEPATH, "RandomAccessFile Read Write test file!!!", 14);
	}catch(IOException e){
		e.printStackTrace();
	}
}

private static byte[] readFromFile(String filePath, int position, int size)
				throws IOException {
	RansomAccessFile file = new RandomAccessFile(filePath, "r");
	file.seek(position);
	byte[] bytes = new byte[size];
	file.read(bytes);
	file.close();
	return bytes;
}

private static void writeToFile(String filePath, String data, int position)
				throws IOException {
	RandomAccessFile file = new RandomAccessFile(filePath, "rw");
	file.seek(position);
	file.write(data.getBytes());
	file.close();
}

[결과]
Welcome!
test

[파일내용]
Welcome!
test
RandomAccessFile Read Write test file!!!

File

  • 파일과 디렉토리를 다루는데 사용되는 클래스
  • 파일의 경로(path)와 디렉토리나 파일의 이름을 구분하는 데 사용되는 구분자가 OS마다 다를 수 있다.
  • File인스턴스를 생성해도 파일이나 디렉토리는 생성되지 않으며 유효하지 않은 파일명이나 디렉토리명이라도 컴파일 예외는 발생하지 않는다.
멤버변수설명
static String pathSeparator편의를 위해 문자열로 표시되는 시스템 종속 경로 구분 문자로 윈도우 “;”, 유닉스 “:”
static char pathSeparatorChar시스템 종속 경로 구분 문자로 윈도우 “;”, 유닉스 “:”
static String separator편의를 위해 문자열로 표시되는 시스템 종속 default-name 구분 문자
static char separatorChar시스템에 따라 달라지는 default-name 구분자로 윈도우 “;”, 유닉스 “:”

메서드명/생성자설명
File(String fileName)지정한 문자열의 파일명을 갖는 파일을 핸들링할 수 있는 FIle 인스턴스를 생성한다. 파일 뿐만 아니라 디렉토리도 같은 방법으로 다룬다.
File(String pathName, String fileName)파일의 경로와 이름을 따로 분리해서 지정할 수 있도록 한 생성자이다.
String getName()파일이름을 문자열로 반환한다.
String getPath()파일의 경로를 문자열로 반환한다.
String getAbsolutePath()
File getAbsolutePath()
파일의 절대경로를 문자열로 반환한다.
파일의 절대경로를 File로 반환한다.
String getParent()
File getParentFile()
파일의 상위 디렉토리를 문자열로 반환한다.
파일의 상위 디렉토리를 File로 반환한다.
String getCanonicalPath()
File getCanonicalFile()
파일의 정규경로를 String으로 반환한다.
파일의 정규경로를 File로 반환한다.
boolean canRead()파일을 읽을 수 있는지 여부를 체크한다.
boolean canWrite()파일을 수정할 수 있는지 여부를 체크한다.
boolean exists()파일이 존재하는지 체크한다.
boolean isAbsolute()파일 또는 디렉토리가 절대경로명으로 지정되었는지 확인한다.
boolean isDirectory()디렉토리인지 확인한다.
boolean isFile()파일인지 확인한다.
boolean isHidden()파일의 속성이 hidden 파일인지 확인한다.
boolean createNewFile()내용이 없는 새로운 파일을 생성한다. 파일이 이미 존재하면 생성되지 않는다.
static File createTempFile(String prefix, String suffix)임시파일을 시스템의 임스 디렉토리에 생성한다.
String File createTempFile(String prefix, String suffix, File directory)임시파일을 시스템의 지정 디렉토리에 생성한다.
boolean delete()파일을 삭제한다.
void deleteOnExit()응용 프로그램 종료시 파일을 삭제한다.
long length()파일의 크기를 반환한다.
String[] list디렉토리의 파일목록을 String 배열로 반환한다.
File[] listFiles()디렉토리의 파일목록을 File 배열로 반환한다.

예제

try{
	//새로운 파일 생성
	File file =
				new File("/NewFileEx02.txt");
	file.createNewFile();
	System.out.println(file);

	//파일이 존재하는지 체크
	boolean bool = file.exists();
	//절대경로 반환
	String path = file.getAbsolutePath();
	if(bool){
		System.out.println(path + " Exists? " + bool);
	}
}catch(Exception e){
	e.printStackTrace();
}

[결과]
/NewFileEx02.txt
/NewFileEx02.txt Exists? true
File targetFile = new File("/streamtest");
File[] files = targetFile.listFiles();

for(int i=0; i<files.length; i++){
	File f = files[i];
	String attribute = "";
	attribute = f.getName();
	if(files[i].isDirectory()){
		attribute += " /Directory";
	}else{
		attribute += " / size : " + f.length() +
								" / canRead : " + f.canRead() +
								" / canWrite : " + f.canWrite() +
								" / isHidden : " + f.isHidden();
	}
	System.out.println(attribute);
}

객체 직렬화(Object Serialization)

  • 직렬화란 객체를 데이터 스트림으로 만드는 것을 의미한다.
  • 객체를 ‘연속적인 데이터’로 변환하는 것. 반대과정은 ‘역직렬화’라고 한다.
  • 객체의 인스턴스변수들의 값을 일렬로 나열하는 것
  • 객체 직렬화를 통해 객체의 모든 인스턴스변수의 값을 저장하는 것(메소드는 포함되지 않는다)

  • Serializable, transient
  • 객체 직렬화 대상 객체는 java.io.Serializable을 implements로 선언해야만 직렬화 가능하다.
public interface Serializable{}

public class Member implements java.io.Serializable{
	String name;
	String password;
	int age;
}

  • 제어자 transient가 붙은 인스턴스변수는 직렬화 대상에서 제외된다. (패스워드 등은 보안상 제외 필요)
public class Member implements java.io.Serializable{
	String name;
	transient String password; //직렬화 대상 제외
	int age;
}

  • Serializable을 구현하지 않은 클래스의 인스턴스, Serializable을 implements하지 않은 상위 멤버들은 직렬화 대상에서 제외
public class Member implements java.io.Serializable{
	String name;
	transient String password; //직렬화 대상 제외
	int age;

	Object o = new Object(); //Object는 직렬화 제외
}
public class ParentMember{
	String name; //직렬화 안 됨
	String password; //직렬화 안 됨
}

public class Member extends ParentMember implements Serializable{
	int age;
}

  • 예제
try{
	ObjectOutputStream oos = new ObjectOutputStream(
		new FileOutputStream("/obj.txt"));
	Member m = new Member("GilDong", "1234", 30);
	oos.writeObject(m);
	System.out.println("객체 저장 완료!");
	oos.close();

	System.out.println("저장된 객체 읽기!");
	ObjectInputStream ois = new ObjectInputStream()
		new FileInputStream("/obj.txt"));
	Member im = (Member)ois.readObject();
	System.out.println("이름: " + im.getName());
	System.out.println("비밀번호: " + im.getPassword());
	System.out.println("나이: " + im.getAge());
	ois.close();
}catch(Exception e){
	e.printStackTrace();
}

[결과]
객체 저장 완료!
저장된 객체 읽기!
이름: GilDong
비밀번호: 1234
나이: 30

ObjectInputStream, ObjectOutputStream

  • ObjectOutputStream은 직렬화(스트림에 객체를 출력) 시 ObjectInputStream은 역직렬화(스트림으로부터 객체를 입력) 시 사용한다. (Chaining Stream)
ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)

  • ObjectOutputStream의 writeObject()로 객체를 파일에 출력하여 직렬화
FileOutputStream fos = new FileOutputStream("objectfile.ser");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new UserInfo());

  • ObjectInputStream의 readObject()로 객체를 역직렬화
FileInputStream fos = new FileInputStream("objectfile.ser");
ObjectInputStream in = new ObjectInputStream(fos);
UserInfo info = (UserInfo)in.readObject();

  • 여러가지 타입의 값을 입출력할 수 있는 메소드를 제공
ObjectOutputStreamObjectInputStream
void defaultWriteObject()
void write(byte[] buf)
void write(byte[] buf, int off, int len)
void write(int val)
void writeBoolean(boolean val)
void writeByte(int val)
void writeBytes(String val)
void writeChar(int val)
void writeChars(String str)
void writeDouble(double val)
void writeFloat(float val)
void writeInt(int val)
void writeLong(long val)
void writeObject(Object obj)
void writeShort(int val)
void writeUTF(String str)
void defaultReadObject()
int read()
int read(byte[] buf, int off, int len)
boolean readBoolean()
byte readByte()
char readChar()
double readDouble()
float readFloat()
int readInt()
long readLong()
short readShort()
Object readObject()
String readUTF()

I/O vs NIO

  • Java NIO(New Input/Output): Java 4 버전부터 java.nio 패키지가 포함되었고, Java 7 버전부터 java.io와 nio 사이의 일관성 없는 클래스 설계를 바로 잡고, 비동기 채널 등의 네트워크 지원을 대폭 강화한 NIO 2 API가 추가되었다.
  • 스트림과 채널: 스트림은 입력과 출력 스트림으로 구분되어 있고, 데이터를 읽기 위한 InputStream, 출력하기 위한 OutputStream을 생성해야한다. NIO는 채널기반으로 양방향으로 입/출력이 가능해서 입력과 출력을 위한 별도의 채널을 생성할 필요가 없다.
구분java.iojava.nio
입출력 방식스트림 방식채널 방식
비동기 방식지원 안 함지원
Buffer 방식Non-bufferBuffer
Blocking/non-blocking 지원Blocking onlyBlocking/Non-blocking 모두 지원
profile
웹퍼블리셔의 백엔드 개발자 도전기

0개의 댓글