스터디/LiveStudy

13주차 과제 : I/O

TheWing 2021. 2. 8. 01:18

13주차 과제: I/O

목표

자바의 Input과 Output에 대해 학습하세요.

학습할 것 (필수)

  • 스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O
  • InputStream과 OutputStream
  • Byte와 Character 스트림
  • 표준 스트림 (System.in, System.out, System.err)
  • 파일 읽고 쓰기

13주차 과제 : I/O

I/O란?

  • Input, Output의 약자로 입출력 통칭하는 용어로 I/O라고 부르며 I/O는 JVM을 기준으로 읽을 때에는 Input을 파일로 쓰거나 외부로 전송할 때는 Output이라는 용어를 사용한다. Input과 Output은 JVM의 기준이다

File과 Files 클래스

File클래스

  • FIle 클래스는 파일 및 경로 정보를 통제하기 위한 클래스이다. File 클래스는 생성한 파일 객체가 가리키고 있는 것이 존재하는지, 파일인지 경로인지, 읽거나, 쓰거나, 실행할 수 있는지 언제 수정되었는지를 확인하는 기능과 해당 파일의 이름을 바꾸고 생성, 삭제하고 전체 경로를 확인하는 등의 기능을 제공한다
  • 이 외에 File 객체가 가리키는 것이 파일이 아닌 경로일 경우에는 해당 경로에 있는 파일의 목록을 가져오거나, 경로를 생성하고 경로를 삭제하는 등의 기능도 있다.

File 객체의 생성자

URI란

  • Uniform Resource Identifier의 약자로 어떠한 리소스를 가리키기 위한 경로를 뜻한다
public class FileSample {
    public static void main(String[] args) {
        FileSample sample = new FileSample();
        String pathName = "C:\\livestudy\\text"; //1
        //String pathName = "/livestudy/text"; //2
        sample.checkPath(pathName);
    }

    public void checkPath(String pathName) {
        File file = new File(pathName); //3
        System.out.println(pathName + " is exists? = " + file.exists()); //4
    }
}
  • 1,2 main() 메소드의 pathName문자열에서 윈도우에선C:\livestudy\text 와 같이 경로를 나타낼때는 역슬래시()를 한 번만 사용한다. 하지만 자바에서 String안에 역슬래시를 한 번만 쓰면 그 뒤에 있는 단어에 따라서 약속한 특수한 기호로 인식한다. 예를 들면 탭을 나타내는 것은 \t 이며 다음 줄을 나타내는 것은 \n 이다. 역슬래시를 나타내기 위해서는 \ 두개의 역슬래시를 연달아 사용해야한다
  • 유닉스 계열에서는 /로 디렉터리를 구분한다. 이러한 모호함을 없애기 위해서File 클래스에 separator라는 것이 static 변수로 존재한다. 따라서 , 다음과 같이 사용하는것이 가장 안전하다
String pathName = File.separator+"livestudy"+File.separator+"text";
  • 2의 경우 유닉스 계열이 사용한다
  • 3 매개변수로 넘어온 경로로 FIle 객체를 생성
  • 4 exists() 라는 메소드를 사용하여 해당 경로가 존재하고 있는지 확인 .
//결과
C:\livestudy\text is exists? = false
  • File 클래스를 사용해서 mkdir()mkdirs() 라는 메소드를 사용한다. 왜 mkdir인지는 윈도우 커맨드 창이나 유닉스의 콘솔에서 디렉터리를 만드는 명령어가 mkdir이기 때문이다
public void checkFileMethods(String pathName) {
    File file = new File(pathName);
    System.out.println(pathName + " is directory? = " + file.isDirectory());
    System.out.println(pathName + " is file? = " + file.isFile());
    System.out.println(pathName + " is hidden? = " + file.isHidden());
}

public void canFileMethods(String pathName) {
    File file = new File(pathName);
    System.out.println(pathName + " can read? = " + file.canRead());
    System.out.println(pathName + " can write? = " + file.canWrite());
    System.out.println(pathName + " can execute? = " + file.canExecute());
}
public void lastModified(String pathName) {
    File file = new File(pathName);
    System.out.println(pathName + " last modified = "+ new Date(file.lastModified()));
}
  • 폴더, 파일, 숨김파일이 있는지?
  • 권한 읽기, 쓰기, 실행 할 수 있는지
  • 수정일자

File 클래스를 이용하여 파일을 처리

public class FileManageClass {
    public static void main(String[] args) {
        FileManageClass sample = new FileManageClass();
        String pathName = File.separator + "livestudy" + File.separator + "text";
        String fileName = "test.txt";

        sample.checkFile(pathName, fileName);
    }

    public void checkFile(String pathName, String fileName) {
        File file = new File(pathName, fileName);
        try {
            System.out.println("Create result = " + file.createNewFile());
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
//결과
Create result = true
  • 파일 생성이 된다
public class FileManageClass {
    public static void main(String[] args) {
        FileManageClass sample = new FileManageClass();
        String pathName = File.separator + "livestudy" + File.separator + "text";
        String fileName = "test.txt";

        sample.checkFile(pathName, fileName);
    }

    public void checkFile(String pathName, String fileName) {
        File file = new File(pathName, fileName);
        try {
            System.out.println("Create result = " + file.createNewFile());
            getFileInfo(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void getFileInfo(File file) throws IOException {
        System.out.println("Absolute path = " + file.getAbsolutePath());
        System.out.println("Absolute file =" + file.getAbsoluteFile());
        System.out.println("Canonical path = " + file.getCanonicalPath());
        System.out.println("Canonical file = " + file.getCanonicalFile());

        System.out.println("Name = " + file.getName());
        System.out.println("Path = " + file.getPath());

    }
}
//결과
Create result = false
Absolute path = C:\livestudy\text\test.txt
Absolute file =C:\livestudy\text\test.txt
Canonical path = C:\livestudy\text\test.txt
Canonical file = C:\livestudy\text\test.txt
Name = test.txt
Path = \livestudy\text\test.txt
  • getFileInfo() 의 메소드 중에서 File로 끝나는 메소드들은 File객체를 리턴하고 Path로 끝나는 메소드들은 전체 경로를 String으로 리턴한다. 그리고 아래에 있는 getName() 메소드는 파일일 경우에는 파일의 이름 , 경로는 전체 경로를 String 으로 리턴 한다.

  • Absolute 와 Canonical 의 결과가 동일 한 것을 볼 수 있다. 절대 경로와 절대적이고, 유일한 경로를 의미한다. file 객체의 경로가 상대 경로일 경우에는 결과가 달라진다.

  • 예시)

    • "C:\livestudy\a" 라는 경로에서 자바를 실행하고, "C:\livestudy\b"라는 경로도 있다고 가정할때 "a" 디렉터리에서 "b" 디렉터리로 이동하려면 "..\b" 와 같이 상대 경로로 움직일 수 있다. 이 경우 Absolute 경로는 "C:\livestudy\a..\b" 가 되고, Canonical 경로는 "C:\livestudy\b" 가 된다. 즉, Canonical은 절대적으로 유일하게 표련할 수 있는 경로를 말한다
  • getPath() 메소드의 결과는 경로뿐만아니라 파일 이름까지 포함되어 있다.

  • 파일이름을 제외한 경로만 확인하려면

      System.out.println("Parent = "+file.getParent());
  • 이렇게 getParent() 메소드를 사용하면 된다

list 메소드

  • 디렉터리에 있는 목록을 살펴보기 위함

public class JPGFileFilter implements FileFilter {
    @Override
    public boolean accept(File file) {
        if (file.isFile()) {
            String fileName = file.getName();
            if(fileName.endsWith(".jpg")) return true;
        }
        return false;
    }
}
public class JPGFilenameFilter implements FilenameFilter {
    @Override
    public boolean accept(File file, String filename) {
        if (filename.endsWith(".jpg")) return true;
        return false;
    }
}
public class FileFilterSample {
    public static void main(String[] args) {
        FileFilterSample sample = new FileFilterSample();
        String pathName = File.separator + "livestudy" + File.separator + "text";
        sample.checkList(pathName);
    }

    public void checkList(String pathName) {
        File file;
        try {
            file = new File(pathName);
            File[] mainFileList = file.listFiles();
//            File[] mainFileList = file.listFiles(new JPGFilenameFilter());
            for (File tempFile : mainFileList) {
                System.out.println(tempFile.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
//결과
test.docx
test.jpg
test.txt
제목 없음.jpg
제목 없음1.jpg
public void checkList(String pathName) {
    File file;
    try {
        file = new File(pathName);
        File[] mainFileList = file.listFiles(new JPGFilenameFilter()); //1
                File[] mainFileList = file.listFiles(new JPGFileFilter()); //2
        for (File tempFile : mainFileList) {
            System.out.println(tempFile.getName());
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
// 1결과
test.jpg
제목 없음.jpg
제목 없음1.jpg
// 2결과
제목 없음.jpg
제목 없음1.jpg
  • 1결과에서는 test.jpg가 폴더임에도 불구하고 목록에 포함되어있다

  • 2결과에서는 제외되었다

  • JDK 7 이상의 버전에서 사용하면 File 클래스 사용보다는 java.nio.file 패키지에 있는 Files 클래스를 사용하는 것이 보다 효과적이다.

스트림 (Stream) / 버퍼 (Buffer) / 채널 (Channel) 기반의 I/O

Stream

  • 자료의 입출력을 도와주는 중간 매개체 역할을 하며 응용 프로그램과 입출력 장치를 연결하는 소프트웨어 모듈
  • 자바에서는 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림을 통해 다룬다

스트림(stream)이란 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름을 의미한다

즉, 스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 한다.

단방향 - 입력, 출력 다 안하고 하나만 가능하다

InputStream과 OutputStream을 사용한다

Buffer

  • 데이터를 전송하는 상호간의 장치에서 고속의 장치와 저속의 장치 간의 속도 차이로 인해 저속의 장치가 작업을 추리하는 동안 고속의 장치가 기다려야하는 현상을 줄여준다

Buffer을 사용하는 이유

하드디스크의 속도는 매우 느리다.

또한 하드디스크 뿐 아니라 키보드,모니터와 같은 외부장치의 속도는 시간이 많이 걸리는 작업이다.

고로 버퍼링없이 키보드가 눌릴 때마다 눌린 정보를 바로 목적지로 이동시켜주는 것보다 중간에 Memory Buffer를 두어서 data를 한데 묶어서 이동시키는 것이 효율적이고 빠르다.

그냥 전송하게 되면 Cpu와 성능 차이가 많이 나서 비효율적이다.

Non-Buffer vs Buffer

  • IO에서는 출력 스트림이 1바이트를 쓰면 입력 스트림이 1바이트를 읽는다. 이런 시스템은 성능이 대체로 느리다. 이것보다는 버퍼를 사용해서 복수 개의 바이트를 한꺼번에 입력받고 출력하는 것이 빠른 성능을 낸다. 그래서 IO는 버퍼를 제공해주는 보조 스트림인 BufferedInputStream, BufferedOutputStream 을 연결해서 사용하기도 한다

NIO는 기본적으로 버퍼를 사용해서 입출력을 하기 때문에 IO보다는 성능이 좋다. 채널은 버퍼에 저장된 데이터를 출력하고, 입력된 데이터를 버퍼에 저장한다.

Scanner

import java.util.Scanner;

public class scratchPad {
    public static void main(String args[]){
        Scanner sc = new Scanner(System.in);
        int input = sc.nextInt();
        System.out.println(input);
    }
}

숫자를 입력 받고 싶을 때 위와 같이 Scanner를 사용해서 편리하게 값을 입력받을 수 있다.

그러나 Scanner는 사용하기 편리하지만 느리다는 치명적인 단점이 있다. 그래서 input 값이 작을 때는 Scanner를 사용하기도 하나 data가 많아졌을 때는 필히 Buffer를 사용해야 한다.

space를 모두 경계로 인식. 가공하기 쉽다. 효율 낮음

Regex(Regular Expression)을 많이 사용하여 알아서 Tokenizing과 Parsing을 해주기 때문에 패턴을 분석하는 데에 있어서 시간을 많이 잡아먹기 때문인데, Regex를 이용하면 패턴을 이용해 문자열을 좀 더 세밀히 분석할 수 있다고 한다.

BufferedReader

일정량 사이즈로 '한 번에' 읽어온 후 버퍼에 보관. 사용자가 요구할 때 버퍼에서 읽어오게 한다.

Scanner의 버퍼 사이즈는 1024 chars VS BufferedReader의 버퍼 사이즈는 8192 chars

enter만 경계로 인식. 받은 데이터가 String으로 고정. 입력받은 데이터를 타입 변환/파싱 해야 함. 많은 데이터를 입력받을 경우 효율 좋음

Buffer

BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //선언
String s = br.readLine(); //String
int i = Integer.parseInt(br.readLine()); //Int
  1. BufferReader 는 오직 String으로만 값을 받지만, 큰 데이터에 있어서는 Scanner보다 효율이 좋다.
  2. 값을 오직 String으로 받기 때문에 String이 아닌 다른값으로 값을 받을 때에는 형변환이 필요하다.
  3. br.readLine()으로 값을 받고, 형변환을 해주어야 한다는 사실만 상기하면 기억하기 쉽다.
  4. public static void main(String args[]) throws IOException BufferedReader 는 예외처리를 꼭 해주어야 한다. try & catch 문을 활용해서 하기도 하지만, 대부분 IOException을 활용해서 한다.

StringTokenizer

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class scratchPad {
    public static void main(String []args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s = br.readLine();

        StringTokenizer st = new StringTokenizer(s); //StringTokenizer 인자값에 입력 문자열 넣음
        int a = Integer.parseInt(st.nextToken()); // 첫번째 호출
        int b = Integer.parseInt(st.nextToken()); // 두번째 호출

        System.out.println(a);
        System.out.println(b);
    }
}

BufferedReader로 받은 data는 Line단위로만 나누어 지기 때문에 , 공백 단위로 나누려면 data를 가공해주어야 한다. 이 때 사용하는 것이 StringTokenizer이다.

값을 변환받은 String을 StringTokenizer에 저장해주고, integer값을 받을거면 parseInt에 st.nextToken()값을 넣어준다.

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); // 선언
String s = "abcdefg"; // 출력할 문자열
bw.write(s+"\n");// 출력
bw.flush(); // 남아있는 데이터를 모두 출력시킴
bw.close(); // 스트림을 닫음

BufferedWriter 의 경우 Buffer을 잡아놓았기 때문에 반드시 flush()와 close를 통해 뒤처리를 해주어야 한다.

또한 println의 ln 처럼 자동개행이 없기 때문에 개행을 하려면 \n을 통해 해주어야 한다.

Channel

  • 양방향 흐름이다
  • ByteChannel, FileChannel 을 만들어서 읽고 쓰기를 한다
  • 양방향이기 때문에 입출력을 위한 별도의 채널을 만들 필요가 없다

특징

  • Socket과 연결 되어 입출력 역할을 수행한다
  • 입출력을 동시에 수행한다.
  • Selector와 연결되어 있고, 하나에 Selector 에는 다수의 채널이 존재할 수 있다.
  • Blocking 된 쓰레드를 깨우거나, 다시 Blocking 할 수 있다.

InputStream과 OutputStream

  • InputStream과 OutputStream은 Java Stream의 부모들이다

InputStream 을 확장한 주요 클래스들

  • AutoInputStream, ByteArrayInputStream, FileInputStream, FilterInputStream, InputStream , ObjectInputStream, PipedInputStream, SequenceInputStream, StringBufferInputStream

OutputStream

public abstract class OutputStream
extends Object
implements Closeable, Flushable
  • Flushable 인터페이스의 메소드는 flush()이다. 일반적으로 어떤 리소스에 데이터를 쓸 때, 매번 쓰기 작업을 "요청할 때마다 저장" 하면 효율이 안좋아진다.
  • flush() 메소드는 "현재 버퍼에 있는 내용을 기다리지 말고 무조건 저장"하라고 시키는 것이다.

OutputStream클래스 메소드

  • InputStream 과 마찬가지로 OutputStream 관련 클래스 메소드도 close() 메소드를 꼭 호출해서 열었던 리소스를 닫아주어야만한다. 그렇지 않으면 애플리케이션에 문제가 발생하는 것은 순식간이다.
  • 버퍼(buffer)는 데이터를 차곡차곡 쌓아 두었다가. 꽉 차야지만 비운다.
  • flush() 는 버퍼 내용을 기다리지 않고 비운다(저장).

Byte와 Character 스트림

Byte Stream

  • Byte 단위로 데이터를 전송

Character Stream

  • Java의 스트림은 기본적으로 바이트단위로 데이터를 전송한다
  • 하지만 자바에서 가장 작은 타입인 char형이 2바이트이므로 1바이트씩 전송되는 바이트 기반 스트림으로는 원활한 처리가 힘든 경우도 있다
  • 자바에서 바이트 스트림뿐만 아니라 문자 기반의 스트림도 별도 제공한다
  • 이러한 문자 기반 스트림은 기존의 바이트 기반에서 스트림에서 InputStream 을 Reader로, OutputStream을 Writer로 변경하면 사용할 수 있다

표준 스트림 (System.in, System.out, System.err)

  • java에서 콘솔과 같은 표준 입출력 장치를 위해 System이라는 표준 입출력 클래스를 정의하고 있다
  • java.lang 패키지에 포함되어 있는 System 클래스는 표준 입출력을 위해 다음과 같은 클래스 변수를 제공한다

표준 입출력 스트림은 자바가 자동으로 생성하므로, 개발자가 별도로 스트림을 생성하지 않아도 사용할 수 있다.

System.out.println

  • 우리가 로그를 찍기위해 System.out.println을 자주 사용하곤한다. println의 경우 성능이 매우 떨어진다. 그 이유는 println을 찍기 위해 CPU를 많이 점유하기 때문이다. 그 뿐만아니라 println을 찍을때까지 애플리케이션이 멈춘다 즉 대기 시간이 발생하게 된다.

한번 코드를 들여다 보자

Synchronized가 걸려 있다. 즉 Thread-safe하지만 여러 쓰레드에서 요청했을시 효율적이지 않다. 왜냐하면 println을 찍기위한 리소스를 많이 점유하고 10만개의 요청이 들어왔다고 가정하면 어마어마한 리소스를 사용하게 되며 애플리케이션이 멈추기 때문에 치명적인 사태를 초래한다

PrintStream 클래스의 print 메소드

PrintStream 클래스의 write 메소드

Writer 클래스의 write 메소드

이렇게 여러 클래스의 IO, NIO를 사용한다

  • PrintSteram, Writer, BufferedWriter, OutputStreamWriter, OutputStream 등등

System.out.println 성능이 왜 안좋은지 모르겠다? 직접해보자

코드는 이렇다

long i = 0;
while (i != 1212131313131313131L) {
    System.out.println("i = " + i++);
}

자 그럼 실행을 해보자

실행 환경은 i7 - 8700 , RAM 16기가 이다

이렇게 CPU를 엄청나게 먹는다.. 만약 서버가 운영중이라면?? 생각만해도 끔찍..

파일 읽고 쓰기

Reader 와 Writer

  • Stream은 byte를 다루기 위한 것이고 Reader와 Writer는 char 기반의 문자열을 처리하기 위한 클래스

Reader 클래스의 선언부

public abstract class Reader
extends Object
implements Readable, Closeable
BufferedReader, CharArrayReader, FilterReader, 
InputStreamReader, PipedReader, StringReader
  • BufferedReader 와 InputStreamReader가 많이 사용된다.

Writer 클래스의 선언부

public abstract class Writer
extends Object
implements Appendable, Closeable, Flushable
  • Appendable이라는 인터페이스가 구현되어 있다. Java 5부터 추가되었으며 각종 문자열을 추가하기 위해 선언되었다.

  • Writer 클래스도 OutputStream 클래스에 선언된 메소드와 대부분 동일하지만 append() 라는 메소드가 존재한다는 점이 다르다.

  • append() 메소드의 매개변수로 넘어가는 CharSequence 인터페이스 이다

텍스트 파일

  • Writer에 있는 write()append() 메소드를 사용하여 데이터를 쓰면, 메소드를 호출했을 때마다 파일에 쓰기 때문에 매우 비효율적이다. 이러한 단점을 보완하기 위해서 BufferedWriter라는 클래스가 있다.

  • 이름 그대로 BufferedWriter는 버퍼라는 공간에 저장할 데이터를 보관해 두었다가, 버퍼가 차게되면 데이터를 저장하게 도와준다. 따라서 매우 효율적인 저장이 가능하다.
import static java.io.File.separator; 
public class ManageTextFile {
    public static void main(String[] args) {
        ManageTextFile manager = new ManageTextFile();
        int numberCount = 10;
        String fullPath = separator + "livestudy" + separator + "text" + separator + "numbers.txt"; //1
        manager.writeFile(fullPath, numberCount); //2
    }
}
  • 1을 보면 파일이 저장될 경로와 파일 이름이 선언되어 있다. 여기서 separatorimport문에 static으로 선언되어 있으므로 이클래스에서 상수처럼 사용할 수 있다.
  • 2이 파일의 경로와 반복할 숫자의 개수를 지정하여 writeFile() 이라는메소드의 매개 변수로 넘긴다.
public void writeFile(String fileName, int numberCount) {
    FileWriter fileWriter = null;
    BufferedWriter bufferedWriter = null;
    try {
        fileWriter = new FileWriter(fileName); //1
        bufferedWriter = new BufferedWriter(fileWriter); //2
        for (int loop = 0; loop <= numberCount; loop++) {
            bufferedWriter.write(Integer.toString(loop)); //3
            bufferedWriter.newLine(); //4
        }
        System.out.println("Write success !!!");
    } catch (IOException ioe) {
        ioe.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (bufferedWriter != null) {
            try {
                bufferedWriter.close(); //5
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
        if (fileWriter != null) {
            try {
                fileWriter.close(); //6
            } catch (IOException ioe) {
                ioe.printStackTrace();
            }
        }
    }
}
//결과
Write success !!!
//생성된 numbers.txt 파일
0
1
2
3
4
5
6
7
8
9
10
fileWriter = new FileWriter(fileName); //1
  1. 파일에 쓰는 작업을 하기 위해서, FileWriter 객체를 생성한다. 이 객체를 생성할 때 IOException이 발생할 수 있기 때문에 try-catch 로 묶어준다. 6에서 객체의 사용이 끝나면 close() 메소드를 호출하여 쓰기위한 객체를 닫아준다
bufferedWriter = new BufferWriter(fileWriter); //2
  1. 파일에 쓰기 작업을 할 때 앞서 버퍼를 사용하기 위해서 BufferWriter 객체를 생성. 5 에서 close() 메소드를 호출하여 버퍼 쓰기 객체를 닫아준다.
bufferedWriter.write(Integer.toString(loop)); //3
  1. bufferedWriter 객체의 write() 메소드를 사용하여 데이터를 입력한다.
bufferedWriter.newLine(); //4
  1. 줄바꿈을 해준다. 이 메소드를 호출하지 않으면 한 줄에 모든결과가 출력된다.

참고 → FileWriter 객체를 생성할 때 IOException이 발생할 수 있다. 이 예외는 여러 경우에 발생할 수 있지만. 일반적으로 다음의 상황에서 발생한다.
매개 변수로 넘어온 파일이름이 파일이 아닌 경로를 의미할 경우
해당 파일이 존재하지는 않지만, 권한등의 문제로 생성할 수가 없는 경우
파일이 존재하지만, 여러가지 이유로 파일을 열 수가 없는 경우

  • FileWriterBufferedWriter 변수를 try 문안에서 선언했다면, finally 에서 close() 메소드를 호출 할 수가 없다.
try {
    int a = 1;
} catch (Exception e) {
    //a ?
} finally {
    //a ?
}
//a ?
  • close() 를 사용할때는 FileWriter, BufferedWriter 순으로 객체를 생성할때 BufferedWriter , FileWriter 순으로 닫아야만 한다.

텍스트 파일을 읽어보자

  • 직접 파일을 열어서 확인해 보려면 FileReaderBufferedReader를 사용하면 된다.
public class ManageTextFile {
    public static void main(String[] args) {
        ManageTextFile manager = new ManageTextFile();
        String fullPath = separator + "livestudy" + separator + "text" + separator + "numbers.txt";
        manager.readFile(fullPath);

    }

    public void readFile(String fileName) {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try {
            fileReader = new FileReader(fileName);
            bufferedReader = new BufferedReader(fileReader);
            String data;
            while ((data = bufferedReader.readLine()) != null) {
                System.out.println(data);
            }
            System.out.println("Read success !!!");
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    }
}
//결과
0
1
2
3
4
5
6
7
8
9
10
Read success !!!
String data;
while ((data = bufferedReader.readLine()) != null) {
    System.out.println(data);
}
public class ManageTextFile {
    public static void main(String[] args) {
        ManageTextFile manager = new ManageTextFile();
        int numberCount = 10;
        String fullPath = separator + "livestudy" + separator + "text" + separator + "numbers.txt";
        manager.writeFile(fullPath,numberCount);
        manager.readFile(fullPath);

    }

    public void writeFile(String fileName, int numberCount) {
        FileWriter fileWriter = null;
        BufferedWriter bufferedWriter = null;
        try {
            fileWriter = new FileWriter(fileName); //1
            bufferedWriter = new BufferedWriter(fileWriter); //2
            for (int loop = 0; loop <= numberCount; loop++) {
                bufferedWriter.write(Integer.toString(loop)); //3
                bufferedWriter.newLine(); //4
            }
            System.out.println("Write success !!!");
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close(); //5
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            if (fileWriter != null) {
                try {
                    fileWriter.close(); //6
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }
    }

    public void readFile(String fileName) {
        FileReader fileReader = null;
        BufferedReader bufferedReader = null;
        try {
            fileReader = new FileReader(fileName);
            bufferedReader = new BufferedReader(fileReader);
            String data;
            while ((data = bufferedReader.readLine()) != null) {
                System.out.println(data);
            }
            System.out.println("Read success !!!");
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
            if (fileReader != null) {
                try {
                    fileReader.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }
        }

    }
}
Write success !!!
0
1
2
3
4
5
6
7
8
9
10
Read success !!!
public void readFileWithScanner(String fileName) {
    File file = new File(fileName);
    Scanner scanner = null;
    try {
        scanner = new Scanner(file);
        while (scanner.hasNextLine()) {
            System.out.println(scanner.nextLine());
        }
        System.out.println("Read success !!!");
    } catch (FileNotFoundException fnfe) {
        fnfe.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        if (scanner != null) {
            scanner.close();
        }
    }

}

//결과
0
1
2
3
4
5
6
7
8
9
10
Read success !!!
  • Scanner 클래스는 텍스트 기반의 기본 자료형이나 문자열 데이터를 처리하기 위한 클래스다.
  • Scanner 클래스의 hasNextLine() 이라는 메소드는 다음 줄이 있는지 확인하기 위해서 사용되며, nextLine() 메소드는 다음 줄의 내용을 문자열로 한 줄씩 리턴해 준다. 따라서, 이 예제에서 while 문의 조건식에는 hasNextLine() 메소드로 다음 줄이 있는지를 확인한 후 nextLine() 메소드로 한 줄씩 읽어들이도록 한 것이다.
  • Java 7에서 제공하는 Files 라는 클래스를 사용하면 다음과 같이 한 줄로 파일을 읽을 수도 있다.
String data = new String(Files.readAllBytes(Paths.get(fileName)));

Reference

'스터디 > LiveStudy' 카테고리의 다른 글

15주차 과제: 람다식  (0) 2021.03.01
14주차 과제: 제네릭  (0) 2021.02.21
12주차 과제 : 애노테이션  (0) 2021.01.31
11주차 과제: Enum  (0) 2021.01.25
10주차 과제: 멀티쓰레드 프로그래밍  (6) 2021.01.18