본문 바로가기
Java/study

IO기반 입출력

by avvin 2019. 4. 22.

IO기반 입출력


자바에서 데이터는 스트림(Stream : 단일방향으로 연속적으로 흘러가는 것)을 통해 입출력된다.


스트림의 종류 : 입력스트림과 출력스트림 


입력 스트림 : 프로그램이 데이터를 입력받을 때 / 프로그램을 기준으로 데이터가 들어옴 / 데이터를 읽어옴

출력 스트림 : 프래그램이 데이터를 보낼 때 / 프로그램을 기준으로 데이터가 나감 /데이터를 출력/ 저장



자바의 기본적인 데이터 입출력 API는 java.io 패키지에서 제공하고 있다.



File 클래스와 입출력 Stream 클래스


 java.io 패키지의 주요 클래스

 

 File

 

 파일 시스템의 파일 정보를 얻기 위한 클래스 


 Console

 

 콘솔로부터 문자를 입출력하기 위한 클래스

 

 InputStream / OutputStream

 

 바이트 단위 입출력을 위한 최상위 입출력 스트림 클래스 

 (모든 종류의 데이터를 받고 보낼 수 있다.)

 

 

XXXInputStema / XXXOutputStream

PrintStream

BufferedInputStream / BufferedOutputStream


 바이트 단위 입출력을 위한 하위 스트림 

(모든 종류의 데이터를 받고 보낼 수 있다.)


Reader / Writer 


 문자 단위 입출력을 위한 최상위 입출력 스트림 클래스

 (오로지 문자만 받고 보낼 수 있도록 특화)

 

FileReader / FileWriter

InputStreamReader / OutputStreamWriter

PrintWriter

BufferedReader / BufferedWriter


 문자 단위 입출력을 위한 하위 스트림 클래스

 (오로지 문자만 받고 보낼 수 있도록 특화)




InputStream 클래스의 주요 메서드


OutputStream 클래스의 주요 메서드


Reader 클래스의 주요 메서드


Writer 클래스의 주요 메서드



콘솔 입출력


System.in 필드


System.in은 InputSream 타입의 static 필드


InputStream inputStream = System.in;


int asciiCode = inputStream.read();


InputStream의 read() 메서드는 십진수 아스키코드가 들어있다.

아스키 코드는 1바이트로 표현되는 256가지의 숫자에 영어 알파벡, 아라비아 숫자, 특수 기호를 매칭하고 있다. 

키보드에서 입력한 문자를 직접 얻고 싶다면 read()로 리턴받은 아스키 코드를 char타입으로 변환하면 된다

char inputChar = (char) inputStream.read();


2바이트를 필요로하는 유니코드를 읽을 땐  read ( byte[ ] b ) 메서드를 사용한다.


byte[] byteData = new byte[15];


read( byteDate ); //byteDate에 읽은 문자를 저장하고 읽은 바이트 개수를 리턴한다.




System.out 필드


System.out은 PrintStream 타입의 static 필드


PrintStream은 OutStream의 하위 클래스이므로 System.out을 OutputStream 타입으로 변환해서 사용할 수 있다.


OutputStream outputStream = System.out;




Console 클래스

(p.1016 예제)


Console 클래스로 콘솔에서 입력받은 문자열을 쉽게 읽을 수 있다.

Console 객체를 얻으려면 System의 정적 메서드인 console()을 호출하면 된다.

Console console = System.console(); 


이클립스에서 실행하면 System.console() 메서드는 null을 리턴하기 때문에 반드시 명령 프롬프트에서 실행해야한다.

cmd에서 실행하는 클래스의 패키지가 시작하는 디렉토리로 이동한 다음에 java 명령어를 사용해야한다.


String readLine() : Enter키 입력 전까지의 모든 문자열을 받는다

char[] readPassword() : 키보드 입력 문자를 콘솔에 보여주지 않고 문자열을 읽음


메서드들은 문자열이나 문자 배열만 리턴한다. -> Console 클래스는 문자열만 읽어온다.



Scanner 클래스

Console 클래스는 콘솔로부터 문자열만 읽어올 수 있고 기본 타입 값을 바로 읽을 수 없다. 

java.util 패키지의 Scanner 클래스를 이용하면 콘솔로부터 기본 타입의 값을 바로 읽을 수 있다.


Scanner 객체를 생성하려면 생성자에 System.in 매개값을 주면 된다.

Scanner = new Scanner( System.in );


Scanner 클래스는 기본 타입의 값을 읽기 위한 메서드를 제공한다.

(기본타입) nextXXX() : XXX타입 값을 읽는다.

boolean nextBoolean() : true/false 값을 읽는다.

String nextLine()  : String 값을 읽는다.




파일 입출력(p.1018)


File 클래스


IO패키지에ㅓ 제공하는 File 클래스는 파일의 정보를 얻어내는 기능, 파일 생성 및 삭제, 디렉토리 생성, 디렉토리에 존재하는 파일 리스트 얻어내는 기능

파일의 데이터를 읽고 쓰는 기능은 지원하지 않는다.


파일의 데이터를 읽고 쓰는 기능 (입출력)은 스트림을 사용해야한다.


디렉토리 구분자 확인

System.out.println(File.separator);


디렉토리 경로가 유효하지 않아도 컴파일 에러나 예외는 발생하지 않는다. 디렉토리나 파일의 존재 여부를 확인하려면


boolean isExist = file.exists()



File객체 생성으로 디렉토리나 파일이 생성되지 않았을 때 


exists() 리턴 값이 false이면 createNewFile(), mkdir() mkdirs() 메서드로 파일 또는 디렉토리 생성할 수 있다.


  • boolean createNewFile() : 새로운 파일 생성

  • boolean mkdir() : 새로운 디렉토리(폴더) 생성

  • boolean mkdirs() : 경로 상에 없는 모든 디렉토리 생성

  • boolean delete() : 파일 또는 디렉토리 삭제



파일 또는 디렉토리 정보 얻어내기


boolean canExecute() : 실행할 수 있는지

boolean canRead() / canWrite() : 읽을 수 있는 파일인지 / 수정 및 저장할 수 있는 파일인지

String getName() : 파일 이름 리턴 

String getParent() : 부모디렉토리 리턴 

File getParentFile() : 부모 디렉토리를 File 객체로 생성 후 리턴

String getPath() : 전체 경로 리턴

boolean isDirectory() : 디렉토리인지 여부

boolean isFile() : 파일인지 여부

boolean isHidden() : 숨김파일인지 여부

long lastModified() : 마지막 수정 날짜 및 시간을 리턴

long length() : 파일의 크기를 리턴

String[ ] list() : 디렉토리에 포함된 파일 및 서브 디렉토리 목록 전부를 String 배열로 리턴

String[ ] list( FilenameFilter filter ) 

: 디렉토리에 포함된 파일 및 서브 디렉토리 목록 중에 FilenameFilter에 맞는 것만 String 배열로 리턴

File[ ] : listFiles() : 디렉토리에 포함된 파일 및 서브 디렉토리 목록 전부를 File 배열로 리턴

File[ ] : listFiles( FilenameFilter fiter ) 

: 디렉토리에 포함된 파일 및 서브 디렉토리 목록 중에 FilenameFilter에 맞는 것만 File 배열로 리턴



FileInputStream


FileInputStream 클래스는 파일로부터 바이트 단위로 읽어들일 때 사용하는 바이트 기반 입력 시스템

바이트 단위로 읽기때문에 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 읽을 수 있다.


FileInputStream을 생성하는 두가지 방법


FileInputStream fis = new FileInputStream("경로"); // 경로에 있는 파일 읽어오기


File file = new File("경로");

FileInputStream fis = new FileInputStream( file ); //"경로" 대신 경로를 담고 있는 File 참조변수로 읽어오기 


읽어오려는데 파일이 존재하지 않으면 FileNotFoundException 발생


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package testAPI;
 
import java.io.FileInputStream;
 
public class TestMain {
 
    public static void main(String[] args) {
 
        try {
            FileInputStream fis = new FileInputStream("C:\\Workspace\\testAPI\\src\\testAPI\\TestMain.java");
 
            int data;
            while ((data = fis.read()) != -1) { //아스키코드(1바이트) 하나씩 읽어와서 data에 저장
 
                System.out.write(fis); //하나씩 읽어온 아스키코드 1바이트씩 출력
            }
            fis.close();
        } catch (Exception e) {
            e.printStackTrace(); 
            //디렉토리나 파일을 생성하고 읽고 쓰고 닫기까지 전부 예외처리할 구분으로 묶어야한다.
        }
    }
}


>>TestMain.java의 코드를 읽어온다.





FileOutputStream


FileInputStream 클래스는 바이트 단위로 데이터를 파일에 저장할 때 사용하는 바이트 기반 출력 시스템

바이트 단위로 읽기때문에 그림, 오디오, 비디오, 텍스트 파일 등 모든 종류의 파일을 저장할 수 있다.


원본 파일을 타겟 파일로 복사하기 

FileInputStream으로 읽은 바이트를 바로 FileOutputStream으로 저장한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package testAPI;
 
import java.io.FileInputStream;
import java.io.FileOutputStream;
 
public class TestMain {
 
    public static void main(String[] args) throws Exception {
        
        String originalFileName = "C:\\test\\test1.txt";
        String targetFileName = "C:\\test\\copyTest1.txt";
        
        FileInputStream fis= new FileInputStream(originalFileName);
        FileOutputStream fos = new FileOutputStream(targetFileName);
        
        
        int readByteNo;
        
        byte[] readBytes = new byte[100];
        
        while((readByteNo= fis.read(readBytes)) != -1) {
            
            fos.write(readBytes);
        }
            
        fos.flush(); // 출력 버퍼에 잔류하는 데이터 완전히 출력
        fos.close();
        fis.close();
 
        System.out.println("복사 완료");    
        
    }
}



파일입출력 스트림 객체는 참조 변수에 담아야 close() 메서드를 호출하여 닫을 수 있다.


flush() 메서드

출력스트림은 내부에 작응 버퍼가 있어서 데이터가 출력되기 전에 버퍼에 쌓여있다가 순서대로 출력된다.

flush()메서드는 버퍼에 잔류하고 있는 데이터를 모두 출력시키고 버퍼를 비우는 역할을 한다.



FileReader


문자 기반 입력 (읽기)스트림 // FileInputStream과 사용 방법은 같다. 


FileWriter


문자 기반 출력 (쓰기)스트림


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package testAPI;
 
import java.io.FileWriter;
 
public class TestMain {
 
    public static void main(String[] args) throws Exception {
 
        FileWriter fw = new FileWriter("C:\\test\\test1.txt"true);
        
        fw.write("FileWrite는 한글로된 문자열을 바로 출력할 수 있다." + "\r\n");
        fw.write("\\r\\n : 캐리지리턴과 라인피드를 함께 써야 줄바꿈 후 개행 시 왼쪽 정렬이 된다.");
 
        fw.flush();
        fw.close();
 
        System.out.println("파일에 문자열 출력 완료");    
        
    }
}
cs





보조 스트림


보조 스트림이랑 다른 스트림과 연결되어 여러 가지 편리한 기능을 제공해주는 스트림


보조스트림 생성

보조스트림 변수 = new 보조스트림( 연결스트림 );


보조스트림은 또 다른 보조 스트림에도 연결되어 스트림 체인을 구성할 수 있다.


보조스트림1 변수1 = new 보조스트림1( 연결스트림 );

보조스트림 변수 = new 보조스트림( 변수1 );



문자단위 스트림으로 변환해주는 보조 스트림 : InputStreamReader / OutputStreamReader


소스 스트림이 바이트 기반 스트림 ( InputStream / OutputStream / FileInputStream / FileOutputStream )이면서 

입출력데이터가 문자라면, Reader와 Writer로 변환하여 다양한 문자(문자셋 종류 지정 가능)를 입출력할 수있게끔 하는 것이 편리하다.


Reader reader = new InputStreamReader( 바이트기반 입력 스트림 );


Writer writer = new OutputStreamWriter( 바이트기반 출력 스트림 );



성능 향상 보조 스트림

바이트 기반 스트림 : BufferedInputStream / BufferedOututStream

문자 기반 스트림 : BufferedReader / BufferedWriter

(p.1035)


프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼(buffer)와 작업함으로써 실행 성능을 향상시킬 수 있다.

예를 들어 프로그램은 직접 하드디스트에 데이터를 보내지 않고 메모리 버퍼에 데이터를 보냄으로써 쓰기 속도가 향상된다.

버퍼는 데이터가 쌓이기를 기다렸다가 꽉 차게 되면 데이터를 한꺼번에 하드디스크로 보냄으로써 출력 횟수를 줄여준다.

 

BufferedReader는 readLine() 메서드 제공



기본 타입 입출력 보조 스트림 : DataInputStream / DataOutputStream


바이트스트림은 기본데이터 타입으로 입출력할 수 없으나  DataInputStream / DataOutputStream과 연결하면 

DataInputStream / DataOutputStream의 readXXX() / writeXXX() 메서드를 이용하여 기본 데이터타입으로 입출력이 가능해진다.

주의할 점 : 데이터 타입에 맞게 순서대로 타입별 메서드를 사용하여 읽어와야한다.



프린터 보조스트림 : PrintStream / PrintWriter (편리하게 데이터 저장)


ptint(), println(), printf() 메서드 제공하는 보조 스트림, 프린트처럼 간편하게 출력하는 기능을 제공한다.

(System.out이 PrintStream타입이다)


int price = 1200

System.out.printf( "상품의 가격 : %d원 \n", price );




객체 입출력(직렬화 / 역직렬화) 보조 스트림 : ObjectInputStream / ObjectOutputStream (★★★)

(p.1044)

메모리에 생성된 객체를 파일 또는 네트워크로 출력하려면 연속적인 바이트기반 스트림으로 출력해야한다.(객체의 직렬화)

반대로 파일에 저장돼있거나 네트워크에 전송된 객체를 읽을 때는 읽어들인 연속적인 바이트를 객체로 복원해야한다 (역직렬화)


문자열이나 기본 타입이 아닌 객체(사용자 정의 타입이나 참조형 객체 )를 담은 스트림의 데이터를 파일이나 네트워크로 입출력하기 위해서는 직렬화가 필요하다. 반대로 직렬화된 데이터를 스트림으로 읽어오기 위해서는 역직렬화 과정이 필요하다. 


writeObject() readObject() 메서드로 직렬화 및 역직렬화


직렬화가 가능한 클래스

(p.1046)

자바는 Serializable 인터페이스를 구현한 클래스만 직렬화할 수 있도록 제한하고 있다

Serializable 인터페이스는 필두, 메서드가 없는 빈 인터페이스이지만 객체를 직렬화할 때 private 필드를 포함한 모든 필드를 바이트로 변환해도 좋다는 표시 역할을 한다.


직렬화의 대상은 필드이므로 역직렬화할 때에도 필드의 값만 복원된다.

static 또는 transient  필드는 직렬화되지 않는다. // transient : serialize 과정에서 제외하고 싶은 경우 선언하는 키워드

역직렬화할 때, 객체의 타입으로 타입캐스팅하여 객체타입 참조변수로 받는다. 

★//필드값만 받아오므로 new를 사용하여 객체를 생성하진 않는다.



serialVersionUID 필드


직렬화된 객체를 역직렬화할 때는 직렬화했을 때와 같은 클래스를 사용해야한다.

클래스의 이름이 같더라도 클래스의 내용이 변경되면 역직렬화에 실패하며 InvalidClassException이 발생한다.

예외 내용은 역직렬화하는 스트림UID(Unique IDentifier)와 현재 클래스의 UID가 다르다는 내용이다.

serialVersionUID 는 같은 클래스임을 알려주는 식별자 역할을 하는데, Serializable 인터페이스를 구현한 클래스를 컴파일하면 자동적으로 serialVersionUID 정적 필드가 추가된다. 문제는 클래스를 재컴파일하면 serialVersionUID 값이 달라진다는 것이다.

이 문제를 해결하려면 serialVersionUID 필드를 명시적으로 선언해주면 된다


static final long serialVersionUID = 정수값; 

//이 정수값은 cmd에서 serialver.exe를 실행하여 자동으로 생성되는 serialVersionUID 값을 구해 복붙하면된다.

cmd에서 실행해서 볼 필요 없이 명시적으로 serialVersionUID필드 자동 생성하기 (http://blog.daum.net/yellowjini/5216095)



부모 클래스가 Serializable 인터페이스를 구현하고 있지 않을 때 : writeObject() / readObject()

(p.1050)














'Java > study' 카테고리의 다른 글

스레드풀(ThreadPool)  (0) 2019.04.24
IO기반 네트워킹  (0) 2019.04.22
병렬 처리 (스트림과 병렬처리 뒷부분)  (0) 2019.04.21
스트림과 병렬처리  (0) 2019.04.20
컬렉션 프레임워크(Collection Framework)  (0) 2019.04.19