자바 입출력 스트림 | 자바 입문강좌 57

자바 입출력 스트림

System.in 입력받기

System.in은 표준 입력 객체입니다. 자바를 처음 배우기 시작한 날 부터 System.out 객체의 println 메소드를 사용해왔습니다. System.in과 out은 표준 입출력 대칭하는 객체입니다. 이들은 인스턴스가 필요없는 static 객체입니다. 지금까지 main 함수에서 인스턴스를 생성하지 않고 사용해왔죠.

아래 예제에서는 System.in 으로 바이트 단위로 키보드에서 데이터를 읽어옵니다.

import java.io.*;

public class Main {
    public static void main(String[] args) {

        int i = 0;
        try {
            while((i = System.in.read())!= -1){
                System.out.print((char)i + "(" + i + ")\n");
            }
        } catch (Exception ec){
            return;
        }
    }
}
abcdefg
a(97)
b(98)
c(99)
d(100)
e(101)
f(102)
g(103)

(10)

Scanner 클래스

Scanner 클래스는 세개의 소스에서 입력을 받을 수 있습니다. 그냥 스트링에서 받거나, 파일에서 입력받고, System.in(표준입력)에서 받을 수 있습니다. Scanner 클래스에서는 System.in 도 입력소스의 하나입니다.

import java.io.*;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) throws FileNotFoundException {
//        다양한 입력소스에서 받을 수 있는 Scanner 클래스
//        1. String source
        System.out.println("[-------- String Source ---------]");
        Scanner sc = new Scanner("Hello Scanner!");
        String myStr1 = sc.nextLine();
        System.out.println("-> 1번 소스 String : " + myStr1);
        sc.reset();
        sc.close();

//        2. File 에서 받음
        System.out.println("[-------- File Source ---------]");
        File myFile = new File("b.txt");

        if(myFile.exists()){
            Scanner sc1 = new Scanner(myFile);
            while(sc1.hasNext()){
                System.out.println(sc1.nextLine());
            }
            sc1.reset();
            sc1.close();
        }

//        3. System.in 에서 받음
        System.out.println("[-------- Standard Input Source ---------]");
        System.out.println("-> 이름을 입력하시오.");
        Scanner sc2 = new Scanner(System.in);
        String text = sc2.nextLine();
        System.out.println("당신의 이름은 " + text + " 입니다");
    }
}
[-------- String Source ---------]
-> 1번 소스 String : Hello Scanner!
[-------- File Source ---------]
비트코인 지갑(Bitcoin Wallet)
...
블록체인 기반의 가상화폐는
블록체인 노드(BlockChain node)라 불리는
여러대의 분산된 컴퓨터에 저장됩니다.
[-------- Standard Input Source ---------]
-> 이름을 입력하시오.
모니터맨
당신의 이름은 모니터맨 입니다

그동안 키보드 입력을 받기 위해 Scanner 를 사용해왔다면 이제는 좀더 다양한 소스에서 데이터를 입력받을 수 있습니다.

FileInputStream 사용해서 입력받기

FileInputStream 은 스트림으로 데이터를 입력받습니다. 스트림이란 추상적인 데이터의 관리를 하기 위한 방식입니다. 데이터를 하나씩 주는게 아니라 물처럼 연속해서 주고 받는 것 입니다.

아래 코드는 스트림으로 파일의 데이터를 읽어와서 출력합니다. while 문에서 보면 바이트 단위로 읽어옵니다. 스트림을 이해하려면 파일이 어떤 상태로 되어 있는지 상상을 해봅니다. 파일에 저장된 순서대로 처음부터 끝까지 (바이트 -1을 만날때까지) 출력합니다.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {

        File newFile = new File("myFile.txt");
        if(!newFile.exists()){
            try {
                newFile.createNewFile();
                System.out.println("-- new file created...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        FileInputStream myFis = null;

        try {
            myFis = new FileInputStream("myFile.txt");

            int o;
            while((o = myFis.read()) != -1){
                System.out.print((char)o);
            }
//
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                myFis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}
abcde
ABCDE

배열 단위로 입력 받기

바이트 단위로 가져오면 아무래도 시간이 많이 걸립니다.

속도를 위해서는 배열에 한꺼번에 담는 것이 좋습니다. 배열을 사용하면 좋은 점이 얼만큼씩 처리할지 정할 수 있습니다. 미리 얼마만큼 배열에 담아놓고 사용한다는 부분에서 보조스트림인 버퍼(buffer) 방식과도 유사합니다만 버퍼는 또 다른 방식입니다.

이렇게 파일을 사용하는 방법을 여러가지로 만들어 놓다 보니까 처음 보는 사람은 혼동스럽습니다. 그럴때는 파일 시스템의 본질에 집중하도록 합니다.

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            FileInputStream fis =
                    new FileInputStream("myFile.txt");
            byte[] readByte = new byte[30];
            int b;
            while((b = fis.read(readByte)) != -1){
                for(byte a : readByte){
                    System.out.print((char)a);
                }
                System.out.println(" | " + b + " Byte read");
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
During the 2016 U. | 30 Byte read
S. presidential election campa | 30 Byte read
ign, WikiLeaks released emails | 30 Byte read
 and other documents from the  | 30 Byte read
Democratic National Committee  | 30 Byte read

FileOutputStream 사용하기

Input 스트림의 반대는 OutpuStream 입니다.

출력이라는 개념은 파일에 데이터를 저장한다. 파일을 쓴다 라고 설명했었습니다.

Output 이 파일에 쓰는 것이고 Input 이 파일의 데이터를 가져오는 것입니다.

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("myOutput.txt");

            for (int i = 65; i < 91; i++) {
                fos.write(i);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

위 코드를 실행시키면 파일에 아스키코드 A부터 Z까지 파일에 씁니다.

배열을 사용한 FileOutputStream

Input 을 사용할 때와 같습니다. 모아서 한번에 파일에 쓰는 것 입니다.

import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try {
            FileOutputStream fos = new FileOutputStream("myOutput.txt", true);

            byte[] b1 = new byte[26];
            for (int i = 0; i < b1.length; i++) {
                b1[i] = (byte) (i + 65);
            }
            fos.write(b1);
            System.out.println("-- Writing completed");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

보조스트림에 한글출력

FileInputStream 은 바이트 단위로 읽지만 보조스트림인 InputStreamReader 를 사용하면 문자 단위로 읽을 수 있습니다. InputStreamReader에다가 FileInputStream을 생성해서 매개변수로 전달합니다.

Buffered 스트림 클래스도 파일작업시 효율을 높여주는 보조스트림입니다. 즉 기본 스트림이 있으면 이 기본스트림에서 넘어오는 자료를 한번 더 처리해주는 클래스들이 보조스트림입니다.

아래 예제는 InputStreamReader로 2바이트인 한글도 읽을 수 있습니다. 보조스트림은 여러개를 함께 사용할 수 있습니다.

import java.io.*;

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

        InputStreamReader isr = new InputStreamReader(
                new FileInputStream("myOutput.txt"));
        int i;
        while((i = isr.read()) != -1){
            System.out.print((char)i);
        }
    }
}

요약

자바 입출력 스트림, 표준 입출력에 대하여 전체적으로 살펴봤습니다. 여러개의 클래스가 종류도 많고 메소드도 다양해서 헷갈리기 쉽습니다. 클래스가 많지만 파일의 본질은 입출력이라는 부분을 포인트로 데이터의 흐름을 생각하며 프로그래밍 한다면 좋을 것 같습니다.

참고문서

Java FileInputStream Class – javatpoint

Java – Files and I/O – Tutorialspoint

Java FileInputStream (With Examples) (programiz.com)

Java FileOutputStream tutorial – writing to files in Java (zetcode.com)

Java FileInputStream tutorial – reading files in Java with FileInputStream (zetcode.com)

Java FileInputStream (jenkov.com)

I/O Streams 자바 입출력 스트림 – 오라클 (oracle.com)

Leave a Comment