[Java] 데이터 입출력
<입출력 스트림>
프로그램에서 데이터가 나가면 출력 스트림
프로그램에 데이터가 들어오면 입력 스트림
프로그램끼리 데이터를 교환하려면 양쪽 모두 입력 스트림과 출력 스트림이 필요하다.
스트림=단방향으로 데이터가 흐르는 것
스트림의 종류
바이트 스트림 = 그림, 멀티미디어, 문자 등 모든 종류의 데이터 입출력할때 사용 (InputStream-입력,OutputStream-출력)
문자 스트림 = 문자만 입출력할 때 사용 (Reader-입력,Writer-출력)
<바이트 출력 스트림>
바이트 단위로 출력하는 스트림
OutputStream클래스 주요 메서드
void write(int b) | 4byte인 int에서 제일 끝 1 byte만 출력 |
void write(byte[ ] b) | 매개값으로 주어진 배열b의 모든 바이트 출력 |
void wrtie(byte[ ] b,int off,int len) | 배열 b의 len개의 바이트 출력. 배열 일부분만 출력 |
flush() ⭐ | 출력 버퍼에 잔류하는 모든 바이트 출력 모든 출력스트림은 버퍼가 있다. write하면 버퍼에 메모리를 쌓는다 그러다가 버퍼가 꽉찼을 때 데이터를 목적지로 보내게 된다. 하지만 보통 버퍼가 커서 데이터를 다 채우는 것이 오래 걸린다. 그래서 조금이라도 찬거 보내기 위해 flush사용 목적지로 보내는 행위를 줄이면 속도가 빨라지기 때문에 이렇게 한다. 각각 하나씩 보내는 것 보다 묶어서 보내는게 전송 비용 더 저렴 |
close() ⭐ | 출력 스트림을 닫고 사용 메모리 해제. 어떤 프로그램이 파일을 사용중이면 다른 프로그램은 쓸 수 없다 그렇기에 사용 후에는 다른 프로그램들이 사용할 수 있게 하기 위해서 close한다. write하고 flush안한 다음 close하면 자동으로 flush하고 close된다. |
FileOutputStream은 OutputStream의 write메서드를 재정의 해서 만든다
그렇기에 write를 쓰는 방법은 같으나 실행 결과나 속도는 다 다르다.
write 메소드 사용
package ch18.sec02.exam02;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
public class WriteExample
{
public static void main(String[]args)
{
try
{
//BYTE ARRAY 전체 출력
OutputStream os=new FileOutputStream("C:/Temp/test0.db");
byte[] arr= {10,20,30};
os.write(arr);
os.flush();//os.close();에서 자동으로 flush되므로 생략해도 문제 없음
os.close();
//BYTE ARRAY 부분 출력
OutputStream os2=new FileOutputStream("C:/Temp/test3.db");
byte[] arr2= {0,10,20,30,40,50,60,70,80,90,100};
os2.write(arr2,2,3);//2index에 시작하여 3개만
os2.flush();
os2.close();
}
catch (FileNotFoundException e)
{ e.printStackTrace(); }
catch (IOException e)
{ e.printStackTrace(); }
}
}
스트림 기반 입출력 처리
write()메소드 - OutputStream은 내부에 작은 버퍼를 가지고 있다. 그 버퍼에 바이트를 저장한다.
flush()메소드 - 출력 버퍼에 잔류하는 모든 바이트 출력
close()메소드 - 출력 스트림 사용 메모리 해제
<바이트 입력 스트림>
InputStream 은 '바이트 입력 스트림'의 최상위 클래스이다.
InputStream 클래스 주요 메서드
method | 설명 |
int read() | 입력 스트림으로 부터 1 byte를 읽은 후 int타입으로 return 더 이상 입력 스트림으로부터 바이트 읽을 수 없다면 -1 return |
int read(byte[]b)⭐ | 한번에 모든 byte를 읽고 b 배열에 저장. 그 후 실제로 읽은 byte수 return |
void close() | 입력 스트림을 닫고 사용 메모리 해제 |
InputStream에서 실제 읽은 양과 요청한 양(버퍼 크기 등)은 차이가 있을 수 있습니다.
이 차이는 스트림의 데이터가 아직 완전히 준비되지 않았거나, 남은 데이터의 양이 요청한 양보다 적을 때 발생
1 Byte 읽기
read()메소드는 입력스트림으로부터 int타입으로 리턴
리턴된 4byte중 끝 1byte에만 데이터가 들어 있다.
ex) 입력스트림에서 5개의 바이트 들어온다면 read()로 1byte씩 5번 읽을 수 있다.
package ch18.sec02.exam02;
import java.io.FileInputStream;
import java.io.InputStream;
public class ReadExample {
public static void main(String[] args)
{
try
{
InputStream is=new FileInputStream("C:/Temp/test0.db");
byte[] data=new byte[100];
while(true)
{
int num=is.read(data);
if(num==-1) break;
System.out.println(data);
}
is.close();
}
catch(Exception e)
{ e.printStackTrace();}
}
}
바이트 배열로 읽기
read(byte[] b) - 입력 스트림으로부터 주어진 배열 길이만큼 바이트를 읽고 배열에 저장 후 읽은 바이트 수 리턴
ex)입력 스트림에 5개의 바이트가 들어온다 한번에 최대3byte를 읽도록 하게 한다면
처음에는 5byte중 3byte읽고 그 다음에는 2byte를 읽는다
그런데 나머지 하나는 원래 첫번째에 있던 값이다..
- 파일 내용 출력
package ch18.sec03;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
public class ReadExample
{
public static void main(String[] args)
{
InputStream is;
try
{
is = new FileInputStream("C:/Temp/test2.db");
byte[] data=new byte[100];
while(true)
{
int num=is.read(data);
if(num==-1) break;
for(int i=0;i<num;i++) {System.out.println(data[i]);}
}
is.close();
}
catch (Exception e)
{ e.printStackTrace(); }
}
}
- 파일 복사 예제
package ch18.sec02.exam03;
import java.io.*;
public class CopyExample
{
public static void main(String[] args)
{
String oFileName="C:/Temp/image1.png";
String tFileName="C:/Temp/image2.png";
try
{
InputStream is=new FileInputStream(oFileName);
OutputStream os=new FileOutputStream(tFileName);
byte[] data=new byte[1000];//byte너무 크면 메모리 너무 많이 쓰는 거라 안좋음
while(true)
{
int num=is.read(data);
if(num==-1)break;
os.write(data,0,num);
}
//is.transferTo(os);//위의 코드와 같은 역할을 한다.
os.flush();
os.close();
is.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
<문자 입출력 스트림>
[Writer]
writer은 문자 출력 스트림의 최상위 클래스로 추상클래스이다.
Writer클래스의 주요 메소드
void write(int c) | 매개값으로 주어진 한 문자를 출력 |
void write(char[] cbuf) | 매개값으로 주어진 배열의 모든 문자 출력 |
void write(char[] cbuf,int off,int len) | 매개값으로 주어진 배열에서 cbuf[off]부터 eln개까지의 문자 출력 |
void write(String str) | 매개값으로 주어진 문자열 출력 |
void write(String str,int off,int len) | 매개값으로 주어진 문자열에서 off순번부터 eln개의 문자 출력 |
void flush() | 버퍼에 잔류하는 모든 문자 출력 |
void close() | 출력 스트림을 닫고 사용 메모리 해제 |
write는 OutputStream과 사용 방법은 동일하지만 출력 단위가 char이다.
메소드 사용 예제
package ch18.sec04;
import java.io.*;
public class WriterExample
{
public static void main(String[] args)throws Exception //JVM이 예외 처리한다
{
Writer writer=new FileWriter("C:/Temp/test1.txt");
String data="오늘은 금요일. 주말에 준비하느라 바빠";
writer.write(data);
writer.flush();
writer.close();
}
}
[Read]
Reader는 문자 입력 스트림의 최상위 클래스로 추상 클래스이다.

Reader클래스의 주요 메서드
int read() | 1개 문자 읽고 return |
int read(char[] cbuf) | 읽은 문자들을 매개값으로 주어진 문자배열에 저장하고 읽은 문자 수 return |
void close() | 입력 스트림 닫고 사용 메모리 해제 |
문자열 사용
import java.io.FileReader;
import java.io.Reader;
public class ReadExample
{
public static void main(String[] args)
{
try
{
Reader reader=new FileReader("C:/Temp/test.txt");
//1char씩 읽기
while(true)
{
int data=reader.read();
if(data==-1)break;
System.out.print((char)data);
}
reader.close();
System.out.println();
//문자 배열 읽기
char[]data=new char[100];
while(true)
{
int num=reader.read(data);
if(num==-1)break;
for(int i=0;i<num;i++) {System.out.println(data[i]);}
}
reader.close();
}
catch(Exception e) {e.printStackTrace();}
}
}
<보조 스트림>
다른 스트림과 연결되어 편한 기능 제공하는 스트림.
보조스트림 단독으로는 사용 못함. 주 입출력스트림이 있어야 달아서 사용 가능
사용 이유
1. 성능. 프로그램 입장에서 더 빠르게 입출력 가능
2. 편리함
- 입출력 스트림과 이어주는 법
보조스트림 변수=new 보조스트림(입출력스트림);
보조 스트림 생성시 생성자 매개값으로 입출력 스트림 제공
ex) FileInputStream(바이트 입력 스트림)에 INputStreamReader(보조 스트림)연결
InputStream is=new FileInputStream("...");
InputStreamReader reader=new InputStreamReader(is);
- 보조스트림에 보조스트림 또 달아서 스트림 체인 구성 가능
보조스트림2 변수=new 보조스트림2(보조스트림1);
보조스트림끼리 이어주는 법
InputStream is=new FileInputStream("...");
InputStreamReader reader=new InputStreamReader(is);
BufferedReader br=new BufferedReader(reader);
- 자주 사용하는 보조 스트림
+) 입력 스트림이나 출력스트림은 단방향이라서 그 용도로만 쓸 수 있다.
보조 스트림 | 기능 |
InputStreamReader | 바이트 스트림을 문자 스트림으로 변환 |
BufferedInputStream, BufferedOutputStream BufferedReader, BufferedWriter |
입출력 성능 향상 |
DataInputStream, DataOutputStream | 기본 타입 데이터 입출력 |
PrintStream,PrintWriter | 줄바꿈 처리, 형식화된 문자열 출력 System.out.println()이 PrintStream 사용한다. class System{ static final PrintStream out=...} 여기서 PrintStream이 제공하는 println사용. |
ObjectInputStream, ObjectOutputStream | 객체 입출력 보내는쪽 받는 쪽 둘다 java로 만들어져있는 경우 객체를 주고 받을 수 있음. 잘 안쓰임 |
<문자 변환 스트림>
기본이 되는 스트림은 Byte Stream이다.
입출력 데이터가 문자면 문자 스트림 (Reader Writer)로 바꿔서 사용하는 것이 좋다.
InputStream을 Reader로 변환
InputStreamReader 보조스트림을 연결하면 된다.
InputStream is =new FileInputStream("C:/Temp/test.txt");
Reader reader=new InputSTreamReader(is);
OutputStream을 Writer로 변환
OutputStreamWriter 보조 스트림을 연결하면됨
OutputStream os=new FileOutputSTream("C:/Temp/test.txt");
Writer writer=new OutputStreamWriter(os);
+) FileWriter 원리
FileOutputStream에 OutputStreamWriter 연결하지 않고 FileWriter직접 생성 가능.
FileWriter는 OutputStreamWriter의 자식 클래스.
이것은 FileWriter가 내부적으로 FileOutputStream에 OutputStreamWriter보조 스트림을 연결한 것임을 의미.
<성능 향상 스트림>
1. 프로그램에서 데이터 전송할 때
CPU, 메모리가 좋아도 하드 디스크 입출력이 늦어지면 프로그램 실행성능은 하드 디스트 처리 속도에 맞춰진다.
프로그램이 1초에 10000자를 전송할때 하드디스트가 1초에 1000자를 저장할 수 있으면
프로그램은 1000자씩 보내야한다. 이런식으로 10000자 다 보내려면 10초가 필요하다.
이걸 개선하기 위해 버퍼를 사용한다.
프로그램은 메모리 임의의 저장공간(버퍼)에 10000자 데이터 보내놓고 다른일을 한다.
그리고 메모리 버퍼에서는 하드 디스크가 감당할 수 있는 1000자씩 보내준다.
이런 방식으로 메모리 버퍼를 사용해서 프로그램의 출력 성능을 높일 수 있다.
2. 프로그램이 데이터를 받을 때
프로그램에서 데이터 입력받을 경우를 생각해보자
입력소스에서 직접 입력 받을 것인데 입력소스는 1초에 1000자를 준다고 치자
프로그램은 1초에 10000자를 처리할 수 있다.
근데 1000자씩 밖에 주지 않으니 10000자를 받으려면 프로그램은 10초를 기다려야한다.
그래서 메모리 버퍼에 입력소스가 보내는 1000자들을 쌓은 뒤
프로그램이 데이터를 필요로 할때 메모리 버퍼에서 왕창 읽어내면 훨씬 효율적이다.
버퍼를 제공하는 보조스트림
BufferedInputStream, BufferedOutputStream - 바이트 스트림
BufferedReader, BufferedWriter - 문자 스트림
ex) Buffer쓸때의 성능 차이 확인해보기 예제
간단한 출력은 버퍼를 쓰지 않아도 되지만, 보통 버퍼를 쓴경우 안쓴 경우보다 훨씬 빠르다
package ch18.sec07;
import java.io.*;
public class BufferExample {
public static void main(String args[]) throws Exception
{
String oFile1="C:/Temp/image1_1.png";
String tFile1="C:/Temp/image1_2.png";
String oFile2="C:/Temp/image2_1.png";
String tFile2="C:/Temp/image2_2.png";
//No Buffer
FileInputStream fis =new FileInputStream(oFile1);
FileOutputStream fos=new FileOutputStream(tFile1);
// Use Buffer
BufferedInputStream bis=new BufferedInputStream(new FileInputStream(oFile2));
BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(tFile2));
//test
long nonBufferTime=copy(fis, fos);
System.out.println(nonBufferTime);
long bufferTime=copy(bis, bos);
System.out.println(bufferTime);
}
public static long copy(InputStream is, OutputStream os) throws Exception
{
long start=System.nanoTime();
while(true)
{
//1byte씩 읽기
int data=is.read();
if(data==-1)break;
os.write(data);
}
long end=System.nanoTime();
return end-start;
}
}
ex) 편리한 기능 관련 예제
printCode1, printCode2를 보면 printCode2가 더 편리한 것을 알 수 있다.
package ch18.sec07;
import java.io.*;
public class ReadLineExample
{
public static void main(String[] args) throws Exception
{
//Test
printCode1();
System.out.println("------------------");
printCode2();
}
public static void printCode1() throws Exception
{
//현재 파일이 저장된 위치 지정
Reader reader=new FileReader("C:\\metanet\\projects\\proj\\src\\ch18\\sec07\\ReadLineExample.java");
char[]data=new char[100];
while(true)
{
int num=reader.read(data);
if(num==-1)break;
String str=new String(data,0,num);
System.out.print(str);
}
reader.close();
}
public static void printCode2() throws Exception
{
Reader reader=new FileReader("C:\\metanet\\projects\\proj\\src\\ch18\\sec07\\ReadLineExample.java");
BufferedReader br=new BufferedReader(reader);
while(true)
{
String line=br.readLine();//더 편리하다.
if(line==null) break;
System.out.println(line);
}
br.close();
}
}
<기본 타입 스트림>
바이트 스트림에 보조스트림(DataInputStream, DataOutpuStream) 연결하면 기본타입값 입출력 가능.
DataInputStream dis=new DataInputStream(바이트 입력 스트림);
DataOutputStream dos=new DataOutputStream(바이트 출력 스트림);
정수값(int value=100)을 1바이트씩 분리해서 byte[]배열에 저장하는 것은 까다롭다
DataIntputStream,DataOutputStream을 사용해서 처리하면 수월하게 가능하다
<프린트 스트림>
문자 데이터 출력할때 주로 사용
출력스트림(PrintStream,PrintWriter)만 있고 입력 스트림 없음
PrintStream,PrintWriter는 print(),println(),printf()메소드를 가지고 있는 보조 스트림이다.
print(Object obj) = obj.toString() return 값을 출력한다.
package ch18.sec09;
import java.io.*;
public class printStreamExample
{
public static void main(String[] args) throws Exception
{
//FileOutputStream fos = new FileOutputStream("C:/Temp/printstream.txt");
//PrintStream ps = new PrintStream(fos);
Writer writer=new FileWriter("C:/Temp/printstream.txt");
PrintWriter ps=new PrintWriter(writer);
ps.print("마치 ");
ps.println("프린터가 출력하는 것처럼 ");
ps.println("데이터를 출력합니다.");
ps.printf("| %6d | %-10s | %10s | \n", 1, "홍길동", "도적");
ps.printf("| %6d | %-10s | %10s | \n", 2, "감자바", "학생");
ps.flush();
ps.close();
}
}
<File과 Files클래스>
java.io =단방향. 대부분 이 기능으로 만들어져 있음
java.nio =양방향. 성능 더 좋음
java.io, java.nio 패키지는 파일과 디렉토리 정보를 가지고 있는 File과 File 클래스를 제공한다.
Files는 File을 개선한 클래스로 더 많은 기능을 가지고 있다.
1. File 클래스
File클래스로부터 File객체를 생성하는 코드
해당 경로를 가지고 file객체를 만들것이라는 것.
굳이 파일경로에 파일이 없어도 됨.
File file=new File("파일 경로");
윈도우에서 File객체 생성하는 코드
File file=new File("C:/Temp/file.txt");
File file=new File("C:\\Temp\\file.txt");
boolean isExist=file.exists();
파일이 없을 경우 (file.exists()가 false) 파일, 폴더 생성
return type | method | explanation |
boolean | createNewFile() | 새로운 파일 생성 File file=new File("C:/Temp/dir1/dir2/dir3"); |
boolean | mkdir() | 새 디렉토리 생성 dir1,dir2이 존재한다면 dir3을 새로만든다. |
boolean | mkdirs() | 경로상에 없는 모든 디렉토리 생성 dir1,dir2,dir3중에 존재하지 않는 것들 다 생성 |
파일이 있을 경우 (file.exists()가 true) 사용할 수 있는 중요한 메소드들
boolean delete() | 파일, 디렉토리 삭제 |
boolean canExecute() | 실행가능한 파일인지 |
String getParent() | 부모 디렉토리 경로 return |
File getParentFile() | File객체 return |
boolean isDirectory() | 디렉토리인지 여부 |
long length() | 파일 크기 return |
File[] listFiles(FilenameFilter filter) | 디렉토리 내부의 모든 파일 목록 return. FilenameFilter로 확장명이 png인것만 가져오기 같이 조건 설정 가능 |
package ch18.sec11;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileExample {
public static void main(String[] args) throws Exception
{
//File 객체 생성
File dir = new File("C:/Temp/images");
File file1 = new File("C:/Temp/file1.txt");
File file2 = new File("C:/Temp/file2.txt");
File file3 = new File("C:/Temp/file3.txt");
//존재하지 않으면 디렉토리 또는 파일 생성
if(dir.exists() == false) { dir.mkdirs(); }
if(file1.exists() == false) { file1.createNewFile(); }
if(file2.exists() == false) { file2.createNewFile(); }
if(file3.exists() == false) { file3.createNewFile(); }
//Temp 폴더의 내용 출력
File temp = new File("C:/Temp");
File[] contents = temp.listFiles();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm");
for(File file : contents)
{
System.out.printf("%-25s", sdf.format(new Date(file.lastModified())));
if(file.isDirectory())
{ System.out.printf("%-10s%-20s", "<DIR>", file.getName()); }
else { System.out.printf("%-10s%-20s", file.length(), file.getName()); }
System.out.println();
}
dir.delete();//directory비어있을 때만 삭제 가능!
file1.delete();
}
}
2. Files 클래스
정적 메소드. 객체없이 쓸 수 있음
기능 | method |
복사 | copy |
생성 | createDirectories, createDirectory, createFile, createLink |
이동 | move |
삭제 | delete |
존재, 검색, 비교 | exists, notExists,find,mismatch |
속성 | size |
디렉토리 탐색 | list |
데이터 입출력 | newInputStream등.. |
Path path=Paths.get("C:/Temp/dir/file.txt");
Path path=Paths.get("C:/Temp/dir","file.txt");
Path path=paths.get("C:","Temp","dir","file.txt");
Path path=Paths.get("dir/file.txt");
Path path=Paths.get("./dir/file.txt");
Path path=Paths.get("../dir2/file.txt");