본문 바로가기

국비 교육

2020.9.15일자 수업 : 파일 입출력

 실습 - 파일 입출력 

 

git/eomcs-java-project-2020/mini-pms-30-a

FileReader와 Scanner

파일의 내용을 읽어들이는 객체FileReader이다. 그러나 여기서 있는 메서드는 두가지 뿐이다. 바이트를 리턴한는 read()byte배열을 받아 그것을 채운 배열을 리턴하는 read() 밖에 없다. 따라서 Scanner라는 플러그인을 장착해서 문자열을 리턴하는 nextLine()4바이트 int값을 리턴하는 nextInt()를 사용할 수 있다. 그럼이제 FileReader와 연결된 Scanner만 사용해도 원하는 작업을 할 수가 있다.

 

훈련 목표

저장된 ./board.csv를 읽어서 각 게시물의 정보를 boardList에 저장하는 loadBoards() 메서드를 정의한다.

 

1단계 : loadBoards() 스태틱 메서드를 정의하고 main 메서드에서 가장 첫줄에 호출한다. loadBoards() 메서드의 구성은 다음과 같다. 

  • File 객체를 만들어 /board.csv 파일에 대한 정보 생성
  • FileReader 객체와 이에 대한 Scanner 차례로 생성
  • 반복문을 돌려서 스캐너로 파일을 한줄씩 읽고 미리 생성해놓은 Board 객체의 필드로 이 정보를 저장
  • 필드를 초기화한 Board 객체를 boardList에 저장
  • nextLine()에서 더 이상 읽을 줄이 없어서 NoSuchElementException 예외를 띄우면 그걸 잡아 반복문에서 빠져나오기
  • FileReader와 Scanner 객체를 만드는 과정에서 오류가 발생하는 경우를 대비해 catch블록을 만들고, finally 블록에서 Scanner, Filereader 객체 순으로 close() 하기
  static void loadBoards() {
    System.out.println("[게시글 파일 로딩]");
    File file = new File("./board.csv");
    
    FileReader out = null;
    Scanner scanner = null;
    
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileReader(file);
      scanner = new Scanner(out);
      
      while (true) {
        try {
          String record = scanner.nextLine();
          
          String[] values = record.split(",");
          
          Board board = new Board();
          
          board.setNo(Integer.parseInt(values[0]));
          board.setTitle(values[1]);
          board.setContent(values[2]);
          board.setWriter(values[3]);
          board.setRegisteredDate(Date.valueOf(values[4])); // "yyyy-MM-DD' -> Date 객체로 변환
          board.setViewCount(Integer.parseInt(values[5]));
          
          // 객체를 list 목록에 추가한다.
          boardList.add(board);
          
        } catch (NoSuchElementException e) {
          break;
        }
      }
      
    } catch (IOException e) {
      System.out.println("파일 출력 중 오류 발생!");
    } finally {
      try {scanner.close();} catch (Exception e) {}
      try {out.close();} catch (Exception e) {}
    }
  }

2단계 : Board와 같은 방식으로 Member, Project, Task에 대한 파일 저장, 로딩 메서드를 모두 정의한다. 첫 테스트를 할 때는 파일에 무언가를 작성하기 전이므로 파일이 존재하지 않아서 예외를 띄우므로 첫 테스트시 일단 각 객체를 add해서 파일을 작성하도록 한다. FileWriter 객체를 생성해야만 파일이 생기므로 파일을 작성한 이후에는 파일이 만들어져있을 것이다. 그러면 다음 테스트부터는 예외 없이 로딩과 저장을 할 수 있다.

  public static void saveMembers() {
    System.out.println("[회원 저장]");
    
    FileWriter out = null;
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileWriter(memberFile);
      
      // 각각 게시글 파일로 출력한다. 
      for (Member member : memberList) {
        String record = String.format("%d,%s,%s,%s,%s,%s,%s\n",
            member.getNo(),
            member.getName(),
            member.getEmail(),
            member.getPassword(),
            member.getPhoto(),
            member.getTel(),
            member.getRegisteredDate().toString());
        out.write(record); 
      }
      
      // 파일 출력 도구 닫기
      // => 이 과정에서 파일 출력 도구의 임시 메모리(Buffer)에 잔류하는 데이터를 마무리로 완전히 출력한다.
    } catch (IOException e) {
      System.out.println("파일 출력 중 오류 발생!");
    } finally {
      try {
        
        out.close();
      } catch (IOException e) {
//        close() 에서 오류가 발생할때는 마땅히 할 것이 없다.
//        그래서 그냥 무시
      }
      
    }
  }
  
  static void loadMembers() {
    System.out.println("[회원 파일 로딩]");
    
    FileReader out = null;
    Scanner scanner = null;
    
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileReader(memberFile);
      scanner = new Scanner(out);
      
      while (true) {
        try {
          String record = scanner.nextLine();
          
          String[] values = record.split(",");
          
          Member member = new Member();
          
          member.setNo(Integer.parseInt(values[0]));
          member.setName(values[1]);
          member.setEmail(values[2]);
          member.setPassword(values[3]);
          member.setPhoto(values[4]);
          member.setTel(values[5]);
          member.setRegisteredDate(Date.valueOf(values[6])); // "yyyy-MM-DD' -> Date 객체로 변환
          
          // 객체를 list 목록에 추가한다.
          memberList.add(member);
          
        } catch (NoSuchElementException e) {
          break;
        }
      }
      
    } catch (IOException e) {
      System.out.println("파일 읽기 중 오류 발생!");
    } finally {
      try {scanner.close();} catch (Exception e) {}
      try {out.close();} catch (Exception e) {}
    }
  }

 실습 - 파일 입출력 구조 리팩토링 

 

git/eomcs-java-project-2020/mini-pms-30-b

팩토리 메서드

  • new 명령을 사용하여 객체를 생성하기 보다는, 메서드를 통해 객체를 리턴 받는다.
  • 인터페이스로 객체 생성 규칙을 정의하여 프로그래밍의 일관성을 제공한다.
  • 또한 객체 생성의 책임을 인터페이스 구현체에게 떠넘긴다.

 

훈련 목표

csv 파일에서 갖고온 문자열을 파싱(분석, 해석)해서 이에 대한 Board객체를 만드는 valuOf 메서드를 정의한다.

 

1단계 : 메서드 추출 - loadBoards() 에서 Board 객체를 생성하고 csv 파일 한 줄에 있는 정보로 객체의 필드를 채우는 코드들을 그대로 뽑아서 하나의 메서드 valueOf() 의 몸체로 담는다.  그리고 원래 코드가 있던 자리에는 valueOf() 메서드를 호출한다.

  static Board valueOf(String csv) {
    String[] values = csv.split(",");
    
    Board board = new Board();
    
    board.setNo(Integer.parseInt(values[0]));
    board.setTitle(values[1]);
    board.setContent(values[2]);
    board.setWriter(values[3]);
    board.setRegisteredDate(Date.valueOf(values[4])); // "yyyy-MM-DD' -> Date 객체로 변환
    board.setViewCount(Integer.parseInt(values[5]));
    
    return board;
  }
  
    static void loadBoards() {
    System.out.println("[게시글 파일 로딩]");
    
    FileReader out = null;
    Scanner scanner = null;
    
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileReader(boardFile);
      scanner = new Scanner(out);
      
      while (true) {
        try {
          String record = scanner.nextLine();
          
          // 객체를 list 목록에 추가한다.
          boardList.add(valueOf(record));
          
        } catch (NoSuchElementException e) {
          break;
        }
      }
      
    } catch (IOException e) {
      System.out.println("파일 읽기 중 오류 발생!");
    } finally {
      try {scanner.close();} catch (Exception e) {}
      try {out.close();} catch (Exception e) {}
    }
  }

2단계 :  메서드 추출(Extract Method) - valueOf(String)와 같은 방식으로, Board 객체의 필드들을 csv 파일 형식의 한 줄로 만드는 toCSVString(Board) 메서드를 만들고, saveBoards()에서 각각을 호출한다.

  static Board valueOf(String csv) {
    String[] values = csv.split(",");
    
    Board board = new Board();
    
    board.setNo(Integer.parseInt(values[0]));
    board.setTitle(values[1]);
    board.setContent(values[2]);
    board.setWriter(values[3]);
    board.setRegisteredDate(Date.valueOf(values[4])); // "yyyy-MM-DD' -> Date 객체로 변환
    board.setViewCount(Integer.parseInt(values[5]));
    
    return board;
  }
  
  static String toCSVString(Board board) {
    return String.format("%d,%s,%s,%s,%s,%d\n",
        board.getNo(),
        board.getTitle(),
        board.getContent(),
        board.getWriter(),
        board.getRegisteredDate().toString(),
        board.getViewCount());
  }
  
    public static void saveBoards() {
    System.out.println("[게시글 저장]");
    
    FileWriter out = null;
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileWriter(boardFile);
      
      // 각각 게시글 파일로 출력한다. 
      for (Board board : boardList) {
        
        out.write(toCSVString(board)); // 번호,제목,내용,작성자,작성일,조회수CRLF
      }
      
      // 파일 출력 도구 닫기
      // => 이 과정에서 파일 출력 도구의 임시 메모리(Buffer)에 잔류하는 데이터를 마무리로 완전히 출력한다.
    } catch (IOException e) {
      System.out.println("파일 출력 중 오류 발생!");
    } finally {
      try {
        
        out.close();
      } catch (IOException e) {
//        close() 에서 오류가 발생할때는 마땅히 할 것이 없다.
//        그래서 그냥 무시
      }
    }
  }
  
  static void loadBoards() {
    System.out.println("[게시글 파일 로딩]");
    
    FileReader out = null;
    Scanner scanner = null;
    
    try {
      // 데이터를 파일에 출력할 때 사용할 도구
      out = new FileReader(boardFile);
      scanner = new Scanner(out);
      
      while (true) {
        try {
          String record = scanner.nextLine();
          
          // 객체를 list 목록에 추가한다.
          boardList.add(valueOf(record));
          
        } catch (NoSuchElementException e) {
          break;
        }
      }
      
    } catch (IOException e) {
      System.out.println("파일 읽기 중 오류 발생!");
    } finally {
      try {scanner.close();} catch (Exception e) {}
      try {out.close();} catch (Exception e) {}
    }
  }

3단계 : 메서드 이동(Move Method) - GRASP PatternInformation Expert 원칙에 따라 Board의 필드와 메서드를 사용하는 valueOf()와 toCSVString()을 Board 클래스로 옮긴다.

GRASP Pattern

  • General Responsibility Assignment Software Patterns
  • Object-Oriented 디자인의 핵심은 각 객체에 책임을 부여(메서드를 위치시킴)하는 것.
  • 책임을 부여하는 원칙들을 말하고 있는 패턴.
  • 9가지의 원칙을 가지고 있다.

Information Expert

  • 책임을 수행할 수 있는 데이터를 가지고 있는 객체에 책임을 부여하는 것.
  • 객체는 데이터처리로직이 함께 묶여 있는 것.
  • 정보 은닉을 통해 자신의 데이터를 감추고 오직 Method로만 데이터를 처리하고, 외부에는 그 기능(책임)만을 제공한다.
출처 : nesoy.github.io/articles/2019-05/GRASP-Pattern
public class App {
// ...
  public static void saveBoards() {
    // ...
    try {
      out = new FileWriter(boardFile);
      
      for (Board board : boardList) {
        
        out.write(board.toCSVString()); 
      }
     //...
  }
  
  public static void loadBoards() {
        // ...
        try {
          String record = scanner.nextLine();
          
          boardList.add(Board.valueOfCsv(record));
          
        } 
        // ...
  }
// ...
}


public class Board {
// ...
  public String toCSVString() {
    return String.format("%d,%s,%s,%s,%s,%d\n",
        this.getNo(),
        this.getTitle(),
        this.getContent(),
        this.getWriter(),
        this.getRegisteredDate().toString(),
        this.getViewCount());
  }
  
  public static Board valueOfCsv(String csv) {
    String[] values = csv.split(",");
    
    Board board = new Board();
    
    board.setNo(Integer.parseInt(values[0]));
    board.setTitle(values[1]);
    board.setContent(values[2]);
    board.setWriter(values[3]);
    board.setRegisteredDate(Date.valueOf(values[4])); // "yyyy-MM-DD' -> Date 객체로 변환
    board.setViewCount(Integer.parseInt(values[5]));
    
    return board;
  }
}

 


 파일 입출력 

git/eomcs-java-basic/src/main/java com.eomcs.io.ex01.Exam0110~Exam0650.java

File 클래스

  • 파일이나 디렉토리 정보를 관리할 수 있게 돕는 클래스이다.
  • 파일이나 디렉토리를 생성,삭제,변경을 가능케 한다.
  • 프로그램에서는 파일이나 디렉토리나 모두 파일로 취급하고, 디렉토리를 다룰 때도 File 클래스를 사용한다.

 

File 클래스의 생성

  • File 클래스를 생성하는 생성자의 파라미터로는 파일명을 줘야하는데, 보통은 경로를 포함한 파일명을 주고, 경로를 포함하지 않으면 자바가 실행되고 있는 현재 위치를 경로로 생각하고 파일을 찾는다.
  • 이클립스에서 프로그램을 실행한다면 . 은 프로젝트 폴더를 가리킨다.
  • 콘솔에서 프로그램을 실행한다면 . 은 현재 명령어를 실행하는 위치를 가리킨다.
  • 상위 경로는 ".."으로 표시한다.

File클래스를 통해 조회할 수 있는 정보

  • getName => 경로를 제외한 파일명
  • getPath => File 객체를 생성할 때 작성했던 파일 경로
  • getAbsolutePath => 절대경로
  • getCanonicalPath => .와..까지 계산된 절대 경로
  • getTotalSpace => 하드 디스크의 총 공간
  • getFreeSpace => 하드 디스크의 남은 공간
  • getUsableSpace => 하드 디스크의 사용가능한 공간
  • isDirectory => 디렉토리인지에 대한 여부
  • isFile => 파일인지에 대한 여부
  • isHidden => 숨겨진 폴더인지에 대한 여부
  • exists => 실제로 존재하는지에 대한 여부
  • canExecute => 실행이 가능되는 지에 대한 여부
package com.eomcs.io.ex01;

import java.io.File;
import java.io.IOException;

public class Exam0110 {
  
  public static void main(String[] args) throws IOException {
    File currentDir = new File(".");
    System.out.printf("폴더명: %s\n", currentDir.getName());
    System.out.printf("경로: %s\n", currentDir.getPath());
    System.out.printf("절대경로: %s\n", currentDir.getAbsolutePath());
    System.out.printf("계산된 절대경로: %s\n", currentDir.getCanonicalPath());
    
    System.out.printf("총크기: %d\n", currentDir.getTotalSpace());
    System.out.printf("남은크기: %d\n", currentDir.getFreeSpace());
    System.out.printf("가용크기: %d\n", currentDir.getUsableSpace());
    
    System.out.printf("디렉토리여부: %b\n", currentDir.isDirectory());
    System.out.printf("파일여부: %b\n", currentDir.isFile());
    System.out.printf("감춤폴더: %b\n", currentDir.isHidden());
    System.out.printf("존재여부: %b\n", currentDir.exists());
    System.out.printf("실행가능여부: %b\n", currentDir.canExecute());
    
  }
}

File로 디렉토리 다루기

디렉토리 생성

1) 경로를 포함하지 않으면 파일명으로 File 객체를 생성하여 mkdir()를 호출하면,  현재 폴더에 디렉토리를 생성한다.

public class Exam0310 {

  public static void main(String[] args) throws Exception {

    File dir = new File("temp");
    if (dir.mkdir()) {
      System.out.println("temp 디렉토리 / 파일를 생성하였습니다.");
    } else {
      System.out.println("temp 디렉토리를 생성할 수 없습니다.");
    }
  }
}

2) 존재하지 않는 디렉토리를 경로로 설정하여 File 객체를 생성하고 mkdir()를 호출하면, 디렉토리가 생성되지 않는다.

public class Exam0320 {

  public static void main(String[] args) throws Exception {

    File dir = new File("temp/a");

    if (dir.mkdir()) {
      System.out.println("temp/a 디렉토리를 생성하였습니다.");
    } else {
      System.out.println("temp/a 디렉토리를 생성할 수 없습니다.");
    }
  }
}
// 결과 !
// temp/a 디렉토리를 생성할 수 없습니다.

3) 특정 디렉토리와 존재하지 않는 그의 상위 디렉토리까지 한번에 생성하고 싶다면, mkdirs() 메서드를 호출하면 된다.

public class Exam0322 {

  public static void main(String[] args) throws Exception {

    File dir = new File("temp2/a");

    if (dir.mkdirs()) {
      System.out.println("temp2/a 디렉토리를 생성하였습니다.");
    } else {
      System.out.println("temp2/a 디렉토리를 생성할 수 없습니다.");
    }
  }
}

// 결과 !
// temp2/a 디렉토리를 생성하였습니다.

디렉토리 삭제

삭제하고자 하는 디렉토리에 대한 File 클래스를 생성한 후, delete() 메서드를 호출하면 삭제된다. 단, 디렉토리 안에 파일이나 또 다른 디렉토리가 있다면 삭제할 수 없다. 존재하지 않는 디렉토리도 삭제할 수 없다.

public class Exam0330 {

  public static void main(String[] args) throws Exception {

    File dir = new File("temp");

    if (dir.delete()) {
      System.out.println("temp 디렉토리를 삭제하였습니다.");
    } else {
      System.out.println("temp 디렉토리를 삭제할 수 없습니다.");
    }
  }
}

File로 파일 다루기

파일 생성

1) 생성할 파일의 경로를 지정하여 File 객체를 만들고 이에 대해 createNewFile()를 호출하면 파일이 해당 경로에 생성된다. 단, 이미 파일이 있다면 다시 생성할 수 없다. 해당 경로가 존재하지 않아도 생성할 수 없으며 이 경우에는 예외를 띄운다.

public class Exam0410 {

  public static void main(String[] args) throws Exception {
    File file = new File("temp2/a/test.txt");

    if (file.createNewFile()) { // 파일 생성
      System.out.println("test.txt 파일을 생성하였습니다.");
    } else {
      System.out.println("test.txt 파일을 생성할 수 없습니다.");
    }
  }
}

2) 존재하지 않는 디렉토리 안에 파일을 생성하고 싶다면 일단 경로를 설정한 File 객체를 생성하고, 객체에 대해 getParentFile()를 호출하여 제일 첫번째 상위 디렉토리에 대한 File 클래스를 리턴받는다. 그리고 그 디렉토리에 대하여 mkdirs()를 호출하면 File 객체가 가리키는 디렉토리를 포함하여 그 상위 모든 디렉토리가 생성될 것이다. 그 후에 마지막으로 원하는 파일을 createNewFile() 메서드를 통해 생성하면 된다.

public class Exam0431 {

  public static void main(String[] args) throws Exception {=
    File file = new File("temp/b/test.txt");

    // File dir = new File(file.getParent());
    File dir = file.getParentFile(); // 위의 코드와 같다.
    System.out.println(dir.getCanonicalPath());
    // 결과 ! /Users/heejin/bitcamp-workspace/bitcamp-java-basic/temp/b

    if (dir.mkdirs()) {
      System.out.println("디렉토리를 생성하였습니다.");
    } else {
      System.out.println("디렉토리를 생성할 수 없습니다.");
    }
    // 결과 ! 디렉토리를 생성하였습니다.
    

    // 위에서 폴더를 생성한 후에 파일을 생성한다.
    if (file.createNewFile()) { // 파일 생성
      System.out.println("파일을 생성하였습니다.");
    } else {
      System.out.println("파일을 생성할 수 없습니다.");
    }
    // 결과 ! 파일을 생성하였습니다.

  }
}

파일 삭제

삭제할 파일의 경로를 지정해서 File 객체를 생성하고, 이에 대해 delete()를 호출하면 파일이 삭제된다. 단 존재하지 않는 파일은 삭제할 수 없고, 경로가 존재하지 않아도 파일을 삭제할 수 없다.

public class Exam0420 {

  public static void main(String[] args) throws Exception {
    File file = new File("temp2/a/test.txt");

    if (file.delete()) { // 파일 삭제
      System.out.println("test.txt 파일을 삭제하였습니다.");
    } else {
      System.out.println("test.txt 파일을 삭제할 수 없습니다.");
    }
  }
}

폴더에 있는 파일이나 디렉토리 정보 알아내기

폴더에 있는 파일이나 하위 디렉토리 이름 배열 받기

원하는 디렉토리의 File 클래스를 생성하여 그에 대해 list()를 호출하면 그 안에 있는 파일이나 하위 디렉토리의 이름을 구분 없이 담은 String 배열을 리턴한다.

import java.io.File;

public class Exam0510 {

  public static void main(String[] args) throws Exception {

    File dir = new File(".");

    String[] names = dir.list();

    for (String name : names) {
      System.out.println(name);
    }
  }
}

폴더에 있는 파일이나 하위 디렉토리에 대한 File 배열 받기

위의 방법과 동일하지만, list() 대신 listFiles()를 호출하면 그 안에 있는 파일과 하위 디렉토리에 대한 각각의 file 객체를 담은 배열을 리턴받을 수 있다. 이렇게 하면 이름 뿐만 아니라 다양한 정보를 출력할 수 있다.

public class Exam0520 {

  public static void main(String[] args) throws Exception {

    File dir = new File(".");

    File[] files = dir.listFiles();

    for (File file : files) {
      System.out.printf("%s %12d %s\n", 
          file.isDirectory() ? "d" : "-", 
          file.length(),
          file.getName());
    }
  }
}

필터 적용하여 폴더 안에 있는 파일 배열 받기

FilenameFilter 구현체를 이용해 파일명에 따라 원하는 파일만 받기

자바형식의 파일만 받고자 하여, 파일명이 ".java"로 끝나는 파일만 받으려고 한다. FilenameFilter 인터페이스를 구현하고, accept(File, string)를 오버라이딩한다. 이 메서드의 파라미터는 두 가지이다.

  • 폴더 안에 있는 모든 파일과 디렉토리의 부모 디렉토리, 즉 list메서드를 호출한 File 객체
  • 폴더 안에 있는 모든 파일과 디렉토리의 이름

리턴 타입은 boolean으로, true를 리턴하면 해당 파일을 배열에 넣을것이며, false를 리턴하면 해당 파일을 배열에 넣지 않을 것이다.

    class JavaFilter implements FilenameFilter {
      @Override
      public boolean accept(File dir/* 부모 경로 */, String name /* 파일,디렉토리 이름 */) {

        if (name.endsWith(".java"))
          return true; // 조회 결과에 포함시켜라!
        return false; // 조회 결과에서 제외하라!
      }
    }

이렇게 FilenameFilter 구현체를 만들고 list메서드를 호출할 때 파라미터로 넣어주면, accept 메서드가 폴더 안의 파일이나 디렉토리를 찾을 때마다 호출된다. 찾은 폴더의 더 아래는 찾지 않는다. 

 

다음과 같이 실행하면 파일명이 ".java"로 끝나는 파일이나 디렉토리의 이름만 들어있는 배열을 리턴받을 수 있다. 그러나 한 가지 단점은, ".java"로 끝나지만 자바 파일이 아닌 디렉토리를 받을 수 있다는 점이다.

public static void main(String[] args) throws Exception {

    class JavaFilter implements FilenameFilter {
      @Override
      public boolean accept(File dir/* 부모 경로 */, String name /* 파일,디렉토리 이름 */) {
        if (name.endsWith(".java"))
          return true; // 조회 결과에 포함시켜라!
        return false; // 조회 결과에서 제외하라!
      }
    }

    File dir = new File(".");

    JavaFilter javaFilter = new JavaFilter();

    String[] names = dir.list(javaFilter);

    for (String name : names) {
      System.out.println(name);
    }
  }
}

 

FilenameFilter 구현체를 이용해 파일명과 파일여부에 따라 원하는 파일만 받기

이전 방법의 단점을 극복하기 위해 accept 메서드에서 각 파일이나 디렉토리를 검사할 때 조건을 하나 더 추가할 수 있다. File 클래스의 생성자는 부모 디렉토리의 file 객체와 이름만 갖고 호출할 수 있다. 따라서 accept() 메서드에서 검사하는 파일에 대한 File 객체를 생성한다. 원래는 파라미터 name에 대해서 endsWith() 를 호출하여 이름만 검사했으나, 이제는 File 객체에 대하여 isFile()를 호출하여 true여야 메서드가 true를 리턴할 수 있도록 조건을 추가한다. 위에서 만든 FilenameFilter의 구현체를 생성하고 파라미터로 list() 메서드를 호출해주면 파일명이 ".java"로 끝나면서 동시에, 파일이 것들의 이름만 담긴 배열을 리턴받을 수 있다.

    class JavaFilter implements FilenameFilter {
      @Override
      public boolean accept(File dir, String name) {

        // 해당 이름이 디렉토리 이름인지 파일 이름인지 알아내려면
        // File 객체를 생성해야 한다.
        //
        File file = new File(dir, name);
        // 디렉토리 정보와 이름을 합쳐 파일 객체를 생성할 수 있다.

        if (file.isFile() && name.endsWith(".java"))
          return true; // 조회 결과에 포함시켜라!
        return false; // 조회 결과에서 제외하라!
      }
    }

FileFilter 구현체를 이용해 파일명과 파일여부에 따라 원하는 파일만 받기

FilenameFilter 인터페이스 뿐만 아니라 FileFilter 인터페이스도 있는데 FilenameFilter와 다른 점은 다음과 같다.

  • listFiles()의 파라미터로 들어갈 수 있다.
  • accept()의 파라미터가 File 객체 하나 뿐이다. 따라서 검사하고 있는 파일이나 디렉토리에 대한 정보를 담은 File 객체가 들어간다.

전에 만든 FilenameFilter의 구현체와 같은 작업을 수행하는 FileFilter의 구현체를 다음과 같이 만들 수 있다. true를 리턴하면 해당 파일에 대한 File 객체가 File[]에 담기고, false를 리턴하면 담기지 않는다.

    class JavaFilter implements FileFilter {
      @Override
      public boolean accept(File file) {
 
        if (file.isFile() && file.getName().endsWith(".java"))
          return true; // 조회 결과에 포함시켜라!
        return false; // 조회 결과에서 제외하라!
      }
    }

이 구현체의 인스턴스를 생성하여 다음과 같이 실행하면 이름이 ".java"로 끝나는 파일에 대한 File 객체만 담긴 배열을 리턴받을 수 있다.

public class Exam0620 {


  public static void main(String[] args) throws Exception {

    File dir = new File(".");

    JavaFilter javaFilter = new JavaFilter();

    File[] files = dir.listFiles(javaFilter);

    for (File file : files) {
      System.out.printf("%s %12d %s\n", file.isDirectory() ? "d" : "-", file.length(),
          file.getName());
    }
  }
}

FileFilter 구현체를 익명 클래스로 구현하기

FileFilter 구현체의 인스턴스는 딱 한번만 생성되므로 익명 클래스로 만들 수가 있다.

public class Exam0630 {


  public static void main(String[] args) throws Exception {

    FileFilter filter = new FileFilter() {
      @Override
      public boolean accept(File file) {
        if (file.isFile() && file.getName().endsWith(".java"))
          return true;
        return false;
      }
    };

    File dir = new File(".");

    File[] files = dir.listFiles(filter);

    for (File file : files) {
      System.out.printf("%s %12d %s\n", file.isDirectory() ? "d" : "-", file.length(),
          file.getName());
    }
  }
}

혹은 다음과 같이 accept의 파라미터 안에서 익명 클래스로 구현할 수도 있다.

public class Exam0640 {


  public static void main(String[] args) throws Exception {

    File dir = new File(".");

    File[] files = dir.listFiles(new FileFilter() {
      @Override
      public boolean accept(File file) {
        if (file.isFile() && file.getName().endsWith(".java"))
          return true;
        return false;
      }
    });

    for (File file : files) {
      System.out.printf("%s %12d %s\n", file.isDirectory() ? "d" : "-", file.length(),
          file.getName());
    }
  }
}