My Profile Photo

DongChanS's blog


수학과 학생의 개발일지


Java (8) - File IO (1) - Node Stream

1) I/O

데이터의 입력과 출력

자바의 I/O : Stream을 통해서 데이터를 주고 받는 구조

1-1. Stream과 Node

  1. Stream :

    데이터의 소스 -> 목적지까지 데이터를 이동시키는 통로

    • Stream의 종류 : 파일 데이터, HTTP 응답 데이터, 키보드 입력(System.in)
  2. Node :

    데이터의 소스 or 목적지

    • Node의 종류 : 키보드, 모니터, 파일, 메모리, 데이터베이스 등등..

1-2. Stream의 종류

java.io 패키지에서 관리

  • 고려해야할 것 : 데이터타입, 노드타입, 방향
  1. 데이터타입

    • byte : binary 데이터도 전송 가능
    • char : 한글의 경우는 한 글자가 2바이트 이상이므로 char기반이 좋음.
  2. 방향

    • 읽느냐 쓰느냐?
      • Reader or Writer
      • Input or Output
  3. 노드의 타입

    • 단방향

      ex) 키보드 (InputStream, Reader), 모니터 (OutputStream, Writer)

    • 양방향

      ex) 파일 (File), 다른 스레드 (Piped), 메모리 이용(Array - ByteArray, CharArray)

1-3. 노드 스트림

Node와 직접 연결된 Stream입니다. (직접적으로 데이터를 받거나, 출력함)

대표적으로 키보드, 모니터, 파일을 다룰 수 있습니다.

  1. 키보드
    • InputStream
    • Reader
    • Scanner
  2. 모니터
    • OutputStream
    • Writer
  3. 파일
    • File 클래스
    • FileInputStream, FileOutputStream
    • FileReader, FileWriter

1-4. 키보드 입력받기

public static void main(String[] args) throws IOException {
    InputStream is = System.in; // 키보드도 하나의 스트림입니다!
    byte[] bytes = new byte[6];
    is.read(bytes);
    for (byte bt : bytes) {
        System.out.println(bt);
    }
}
abcd (입력)
---(출력들)---
97
98
99
100
10
0

InputStream 객체의 메서드 read를 통해서 데이터를 읽을 수 있는데,

read 함수는 오버라이딩이 되어있기 때문에 offset같은거도 설정할 수 있고, bytes라는 배열에 저장하도록 할 수도 있고, 아무튼 그렇다..

출력된 값들을 보면, a -> 97, b -> 98, c -> 99, d -> 100 그리고 마지막으로 \n -> 10으로 인식되기 때문에 bytes에는 [97, 98, 99, 100, 10, 0]이 들어있습니다.

그런데, 뭔가 문자인데 숫자로 인식되는게 불편합니다.

이를 위해서 숫자를 문자로 번역할 수 있는 일종의 통역사가 필요합니다.

=> 그런 통역사 역할을 보조 스트림(Auxiliary Stream)이 할 수 있다고 합니다.

1-5. Scanner

그런데, Scanner가 추가되면서 더 쉽게 처리할 수가 있다고 합니다.

import java.util.Scanner;

public class ScannerTest {
    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        for (int i=0; i<5; i++){
            System.out.println(sc.nextInt());
        }
    }
}
1 a b c (인풋)
1
Exception in thread "main" java.util.InputMismatchException
	at java.util.Scanner.throwFor(Scanner.java:864)
	at java.util.Scanner.next(Scanner.java:1485)
	at java.util.Scanner.nextInt(Scanner.java:2117)
	at java.util.Scanner.nextInt(Scanner.java:2076)
	at Tutorial.ScannerTest.main(ScannerTest.java:8)

sc.nextInt() 이외에도 nextLine()같은 여러 메소드가 있기 때문에 타입만 잘 지정을 하면 출력을 해줍니다.

만약 타입이 틀리면 출력되지 않고 에러를 반환해줍니다.

그렇지만 이게 다음에 소개할 보조스트림을 이용한 방식보다는 처리 속도가 느린 경우가 많기 때문에 어찌됬건 보조스트림에 대해서 알고 있어야 합니다.

1-6. 파일 입출력

  1. File 클래스

    파일에 대한 정보를 담고 있는 객체, but 파일을 읽고 쓰는 것은 불가능.

    파이썬으로 치면 일종의 OS 라이브러리랑 비슷한 느낌인것 같네요.

    사용예제1. 주어진 위치에 파일을 생성하기.

    public static void main(String[] args) throws IOException, URISyntaxException{
            String dirName = "c:" + File.separator + "Temp" + File.separator + "mydir";
            File file1 = new File(dirName);
            File file2 = new File(dirName, "test2.txt");
            File file3 = new File(new URI("file:///c:/Temp/test4.txt"));
            file1.mkdir();
            file2.createNewFile();
            file3.createNewFile();
            file3.deleteOnExit(); // 어플이 종료되면서 삭제됨.
        }
    
    • File.separator를 사용하기 : OS에 따라 구분자가 달라지는 것을 해결하기 위함
    • deleteOnExit : 어플리케이션이 종료될 때 자동으로 파일, 디렉토리를 삭제함.

    사용예제2

    현재 디렉토리 -> 자식파일들을 획득 -> 파일의 정보를 출력하기.

    public static void main(String[] args){
        File currentDir = new File(".");
        if (currentDir.exists()){
            File[] childs = currentDir.listFiles();
            for (File child : childs){
                String name = child.getName();
                System.out.println(name);
            }
        }
    
    • .listFiles() : 그 디렉토리에 있는 파일들의 리스트를 가져옴.
    1. 현재 파일트리구조

      C:.
      ├─.idea
      ├─markdown
      ├─out
      │  └─production
      │      └─untitled104
      │          ├─com
      │          │  └─company
      │          ├─META-INF
      │          └─Tutorial
      └─src
          ├─Algorithm
          ├─com
          │  └─company
          └─Tutorial
      
    2. 출력된 내용

      .gitignore
      .idea
      markdown
      out
      src
      untitled104.iml
      

    src의 Tutorial 안에 있는 클래스에서 실행하였으나, 출력은 src가 아니라 윗쪽을 기준으로 출력이 되고 있습니다.

  2. FileInputStream, FileOutputStream, FileReader, FileWriter

    결론적으로 파일을 읽고 쓰기 위해서는 얘네들이 필요합니다.

    ex) FileInputStream input = new FileInputStream(file) : 주어진 file객체의 내용을 읽기 위한 Stream

  • 예제 : c:/Windows/explorer.exe 파일을 c:/Temp로 복사하기

    • 두개의 파일 객체를 열기
    • Source의 파일 객체를 읽어들이고, Target의 파일 객체에 쓰기
    public static void main(String[] args){
            String src_path = "C:" + File.separator + "Windows" + File.separator + "explorer.exe";
            String tg_path = "C:" + File.separator + "temp" + File.separator + "explorer.exe";
            File source = new File(src_path);
            File target = new File(tg_path);
            try {
                FileInputStream input = new FileInputStream(source);
                FileOutputStream output = new FileOutputStream(target);
                byte[] buffer = new byte[100];
                int read;
                while ((read = input.read(buffer)) > 0 ){ 
                    output.write(buffer, 0, read);
                }
            } catch (IOException e){
                e.printStackTrace();
            }
        }
    
    1. input.read(buffer)
      • 파일의 값을 읽어서 buffer에 추가한다.
      • 이 함수의 리턴값은 데이터의 byte 개수입니다.
    2. output.write(buffer, 0, read)
      • 0부터 read인덱스까지 buffer배열의 값을 파일에 씁니다.
    3. 자바에서는 특이한 while문이 가능합니다. while문 조건을 체크할 때도 그 값을 변수로 넣어버릴 수 있네요.
comments powered by Disqus