Post

[JAVA] java.nio 패키지의 활용

java.nio

  • nio는 New Input Output의 약자
  • 기존 java.io를 개선한 새로운 입출력 패키지
  • JDK 7부터는 파일 I/O를 개선한 NIO2 등장
    • java.nio와 그것의 서브 패키지 형태
    • Path 인터페이스, Files, FileChannel 클래스 제공
    • 비동기식 파일 입출력을 위한 AsynchronousFileChannel 클래스 제공
      • non-blocking 방식 파일 입출력

Path 인터페이스

  • java.io.File 클래스보다 다양한 기능을 제공하는 인터페이스
  • java.nio.file 패키지에 존재
  • 파일 시스템에 존재하는 파일이나 디렉터리에 해당하는 경로를 절대/상대 경로로 표현
  • java.nio.file.Files 클래스의 static 메소드를 이용해 Path 객체를 조작 가능
    • 생성, 읽기, 쓰기, 복사 ,이동 등
  • Path 객체 생성 방법
    • Path p = Paths.get("C:\\tmp\\foo");
    • 파일이나 디렉터리의 절대/상대 경로를 명시해야 함
메소드설명
int compareTo(Path other)두 경로를 비교하여 같으면 0을 반환함
Path getFileName()디렉터리 또는 파일의 이름을 Path 객체로 리턴함
FileSystem getFileSystem()Path 객체를 만들어 준 FileSystem 객체를 리턴함
Path getName(int index)경로에서 index에 해당하는 이름을 Path 객체로 리턴함
int getNameCount()경로에 포함된 원소의 개수를 리턴함
Path getParent()부모 경로를 Path 객체로 리턴함
Path getRoot()루트 디렉터리를 Path 객체로 리턴함
Iterator iterator()경로에 존재하는 모든 디렉터리 또는 파일의 이름을 Iterator 객체로 리턴
File toFile()Path 객체를 File 객체로 변환하여 리턴함
String toString()Path 객체를 문자열로 변환하여 리턴함
1
2
3
4
5
6
7
8
9
10
11
12
13
Path path = Paths.get("C:\\windows\\system32\\drivers\\etc\\hosts");
System.out.println("파일 이름:" + path.getFileName());
System.out.println("상위 폴더:" + path.getParent().getFileName());
System.out.println("경로 길이:" + path.getNameCount());

for (int i = 0; i < path.getNameCount(); i++) {
  System.out.print(path.getName(i) + "\\");
}
Iterator<Path> it = path.iterator();
System.out.print("\n현재 경로: ");
while (it.hasNext()) {
  System.out.print(it.next().getFileName() + "\\");
}

FileSystem 클래스와 FileStore 클래스

  • FilsSystem 클래스
    • 파일 시스템에 대한 인터페이스 제공
    • FileSystems.getDefault(): 기본 파일 시스템을 리턴함
    • Iterable<FileStore> getFileStores(): 하나 이상의 파일 스토어로 구성된 Iterable을 리턴함
  • FileStore 클래스
    • 저장소(파티션 또는 볼륨)을 표현함
    • name(), type(), getTotalSpace(), getUnallocatedSpace(), getUsableSpace() 등의 메소드 제공
1
2
3
4
5
6
7
8
FileSystem fs = FileSystems.getDefault();
for (FileStore store: fs.getFileStores()) {
  System.out.println("드라이브 이름: " + store.name());
  System.out.println("파일 시스템: " + store.type());
  System.out.println("전체 공간: " + store.getTotalSpace());
  System.out.println("사용 중인 공간: " + (store.getTotalSpace() - store.UnallocatedSpace()) + "bytes");
  System.out.println("사용 가능한 공간: " + store.UnallocatedSpace() + "bytes");
}

Files 클래스

  • 파일 조작 기능을 제공하는 static 메소드를 제공함
    • 이 메소드는 Path 객체를 인자로 가짐
  • 파일의 읽기와 쓰기 메소드 제공
  • 파일이나 디렉터리의 검사, 생성, 삭제, 복사, 이동, 속성 관리 메소드 제공
1
2
3
4
5
6
7
8
9
10
Path path = Paths.get("C:\\Java");
DirectoryStream<Path> ds = Files.newDirectoryStream(path);
for (Path p: ds) {
  if (Files.isDirectory(p)) {
    System.out.println("디렉터리: " + p.getFileName());
  } else {
    System.out.println("파일: " + p.getFileName());
    System.out.println("파일 사이즈: " + Files.size(p));
  }
}

버퍼

  • 데이터가 입출력될 때 발생할 수 있는 지연을 방지하는 역할
  • 프로그램은 버퍼로부터 데이터를 입력받거나 출력받음
  • 기본형 값을 저장하는 데이터 보관소
    • 채널 입출력에 사용되며 버퍼 단위로 입출력 가능
  • Buffer는 추상 클래스로 java.nio 패키지에 존재함
  • boolean을 제외한 모든 기본형에 대해 Buffer의 서브 클래스가 존재함
    • ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, IntBuffer …
1
2
3
4
// 버퍼의 생성 방법
Buffer buffer = ByteBuffer.allocate(1024*1024);
byte[] barray = new byte[100];
Buffer buffer2 = ByteBuffer.wrap(barray);
메소드설명
Buffer mark()mark를 position의 값으로 설정
Buffer reset()position을 mark의 값으로 설정
Buffer rewind()position을 0으로 바꾸고 mark를 삭제함
처음부터 다시 읽기를 준비하는 것
Buffer filp()limit를 position 값으로 설정하고 position은 0으로 변경함.
버퍼에 쓰기를 끝내고 버퍼 읽기를 준비하는 것
Buffer clear()버퍼를 초기 상태로 돌림
새로운 쓰기를 준비하는 것
int capacity()현재 capacity 값을 리턴
int position()현재 position 값을 리턴
int limit()현재 limit 값을 리턴

버퍼의 속성

0 <= mark <= position <= limit <= capacity

  • capacity: 버퍼의 크기(데이터의 개수). 생성될 때 정해짐
  • position: 읽기나 쓰기가 적용되는 위치
  • limit: 읽거나 쓸 수 없는 최초 위치
  • mark: reset 되었을 때 position이 가리킬 위치

버퍼 읽기와 쓰기

  • Buffer의 서브 클래스에서 제공
  • 상대적 읽기/쓰기 메소드
    • 현재 position의 위치에서 읽기 또는 쓰기를 수행하며 읽거나 쓴 요소만큼 position 값이 증가함
    • byte get(), ByteBuffer get(byte[])
    • ByteBuffer put(), ByteBuffer put(byte[])
  • 절대적 읽기/쓰기 메소드
    • 읽거나 쓸 위치를 매개변수 index로 지정
    • position 값에 영향을 주지 않음
    • byte get(int index)
    • ByteBuffer put(int index, byte b)

FileChannel 클래스

  • java.io 패키지의 파일 관련 입출력 스트림을 대체하기 위한 클래스
  • java.nio.channels 패키지에 존재
  • 파일에 대한 읽기와 쓰기를 제공
  • 멀티스레드 환경에서도 안전하게 사용할 수 있음
  • 객체 생성 방법
    • FileChannel.open(Path path, OpenOption ... options)
    • 옵션은 StandardOpenOption.READ, CREATE, WRITE, APPEND 등
    • FileInputStream, FileOutputStream 또는 RandomAccessFile 객체에서 getChannel() 메소드로 생성 가능
  • 읽기/쓰기 메소드
    • int read(ByteBuffer dst)
    • int write(ByteBuffer src)

WatchService 인터페이스

  • 어떤 대상을 정한 후 변화나 이벤트가 생기는 것을 감시(watch)하는 인터페이스
  • java.nio.file 패키지에 존재
  • 감시 대상은 Watchable 객체로, register() 메소드를 사용하여 WatchService 객체에 감시 대상으로 등록함
  • 디렉터리를 표현하는 Path 객체의 생성, 삭제, 수정 등의 변화를 감지

감시 절차

(1) WatchService 객체 생성

1
WatchService ws = FileSystems.getDefault().newWatchService();

(2) 감시 대상 디렉터리를 WatchService에 등록

1
2
3
4
Path path = Paths.get("c:\\java\\temp");
path.register(ws, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_DELETE,
                  StandardWatchEventKinds.ENTRY_MODIFY);

(3) WatchService는 take() 메소드를 호출하여 감시함

1
2
3
4
5
6
7
WatchKey key = ws.take();

for (WatchEvent<?> event: key.pollEvents()) {
  WatchEvent.Kind k = event.kind(); // 이벤트 종류
  Path p = (Path) event.context(); // 파일 이름
}
boolean valid key.reset(); // 계속 감시하기 위해 리셋
  • 이벤트가 발생하면 take()가 리턴하는 WatchKey 객체를 이용하여 이벤트를 처리
  • WatchKey는 감시 상태, 감시 대상 디렉터리, 이벤트 정보를 가짐
    • pollEvents()를 호출하여 WatchEvent 객체를 얻고 어떤 변화가 생겼는지 알 수 있음

예제: WatchService 인터페이스 이용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
WatchService ws;
ws = FileSystems.getDefault().newWatchService();
Path path = Paths.get("c:\\java\\temp");
path.register(ws, StandardWatchEventKinds.ENTRY_CREATE,
                  StandardWatchEventKinds.ENTRY_DELETE,
                  StandardWatchEventKinds.ENTRY_MODIFY);
while (true) {
  WatchKey key = ws.take();

  for (WatchEvent<?> event: key.pollEvents()) {
    WatchEvent.Kind k = event.kind();
    Path p = (Path) event.context();
    if (k == StandardWatchEventKinds.ENTRY_CREATE) {
      System.out.println("File: " + p.getFileName() + " is created.");
    } else if (k == StandardWatchEventKinds.ENTRY_DELETE) {
      System.out.println("File: " + p.getFileName() + " is deleted.");
    }
  }
  boolean valid = key.reset();
  if (!valid) break;
}
This post is licensed under CC BY 4.0 by the author.