参考メモ/Java7のnio2のファイル操作の便利機能(basename,dirname,realpath相当とファイルの簡易読み書き)

Java7で導入されたNIO.2のファイルI/Oでいろいろ便利になったので、前から気になってた「Javaで他の言語であるbasename,dirname,realpath相当はどうするの？」や、「ファイル内容の一気読み/一気書き、もうちょっと簡単に書けないかな？」というのを試したメモです。

NIO.2の新しいファイル操作については、公式チュートリアルの以下が非常によくまとめてくれてました。この記事で紹介してる以外の機能など、いろいろ「え、こんなのも出来るように！？」というのがあるので、一度ざっと見てみると面白いです。

- File I/O (Featuring NIO.2) (The Java™ Tutorials > Essential Classes > Basic I/O)
  - https://docs.oracle.com/javase/tutorial/essential/io/fileio.html

※Java7からの機能の解説記事ではありますが、サンプルコードは記事執筆時点でのメジャーバージョンであるJava8で行ってます。参考URLなどもJava8のものを載せてますので、ご了承ください。

```
掲載サンプルコードのコンパイル＆実行確認環境：
$ java -version
java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
```

***

なお、本記事では basename,dirname,realpath についてPHP関数の動きをイメージしてます。ですので、PHPプログラマがJavaを使うときに「あれ、PHPでのbasename/dirname/realpathはJavaだとどう書くんだっけ？」という時に本記事はぴったりかもです。

- http://php.net/basename
- http://php.net/dirname
- http://php.net/realpath

# basename,dirname相当

最初の紹介で申し訳ないですが、これについてはJava6以前の `java.io.File` クラスでも提供されてたようで・・・自分が知らないだけだったようです（汗）。

```java:FileNames.java
import java.io.*;
import java.nio.file.*;

public class FileNames {
    public static void main(String[] args) throws Exception {
        String fname = args[0];

        File oldf = new File(fname);
        System.out.println("File.separator=" + File.separator);
        System.out.println("File.pathSeparator=" + File.pathSeparator);
        System.out.println("File.getAbsolutePath()=" + oldf.getAbsolutePath());
        System.out.println("File.getCanonicalPath()=" + oldf.getCanonicalPath());
        System.out.println("File.getName()=" + oldf.getName());
        System.out.println("File.getParent()=" + oldf.getParent());
        System.out.println("File.getPath()=" + oldf.getPath());

        Path newp = Paths.get(fname);
        System.out.println("Path.getFileName()=" + newp.getFileName().toString());
        System.out.println("Path.getParent()=" + newp.getParent().toString());
        if (newp.getRoot() != null) {
            System.out.println("Path.getRoot()=" + newp.getRoot().toString());
        }
        System.out.println("Path.normalize()=" + newp.normalize().toString());
        System.out.println("Path.toAbsolutePath()=" + newp.toAbsolutePath().toString());
        System.out.println("Path.toRealPath()=" + newp.toRealPath().toString());
    }
}
```

例：

```
$ java FileNames ../../java/hoge.java 
File.separator=/
File.pathSeparator=:
File.getAbsolutePath()=/home/msakamoto/work/java/nio2-fileio/../../java/hoge.java
File.getCanonicalPath()=/home/msakamoto/work/java/hoge.java
File.getName()=hoge.java
File.getParent()=../../java
File.getPath()=../../java/hoge.java
Path.getFileName()=hoge.java
Path.getParent()=../../java
Path.normalize()=../../java/hoge.java
Path.toAbsolutePath()=/home/msakamoto/work/java/nio2-fileio/../../java/hoge.java
Path.toRealPath()=/home/msakamoto/work/java/hoge.java
```

## basename,dirname,realpath相当

大体のマッピングです。

- basename相当(ファイルパスの内、ファイル名だけを抜き出す)
  - `File.getName() : String`
  - `Path.getFileName() : Pathオブジェクト`
- dirname相当(ファイルパスの内、ファイルの所属するディレクトリ名を抜き出す)
  - `File.getParent() : String`
  - `Path.getParent() : Pathオブジェクト`
- realpath相当(相対パスを絶対パスに変換)
  - `Path.toRealPath() : Pathオブジェクト`

## 参考

Java8 API :

- https://docs.oracle.com/javase/jp/8/docs/api/java/io/File.html
- https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Path.html

その他：

- file - Java: splitting the filename into a base and extension - Stack Overflow
  - http://stackoverflow.com/questions/4545937/java-splitting-the-filename-into-a-base-and-extension
- パスからファイル名を取得するには (basename) | 入出力・ファイル操作 | プログラミング言語の比較 | hydroculのメモ
  - https://hydrocul.github.io/wiki/programming_languages_diff/io/basename.html
- Java file dirname and basename | Xinotes
  - http://www.xinotes.net/notes/note/774/
- file io - Getting the directory name in java - Stack Overflow
  - http://stackoverflow.com/questions/3009981/getting-the-directory-name-in-java

# ファイル読み書きのショートカット

Filesクラスに便利なのが用意されました。

- https://docs.oracle.com/javase/jp/8/docs/api/java/nio/file/Files.html

- 全バイトを一気読み
  - `Files.readAllBytes(Path path)`
- テキストを一行ずつ一気読み(次のコードポイントを改行として認識 : \u000D + \u000A = CRLF, \u000A = LF, \u000D = CR)
  - `Files.readAllLines(Path path)`
  - `Files.readAllLines(Path path, Charset cs)`
- 全バイトを一気書き
  - `Files.write(Path path, byte[] bytes, OpenOption... options)`
- 文字列のリストを一行ずつ一気書き(改行コードは `line.separator` システムプロパティ)
  - `Files.write(Path path, Iterable<? extends CharSequence> lines, Charset cs, OpenOption... options)`
  - `Files.write(Path path, Iterable<? extends CharSequence> lines, OpenOption... options)`


一行ずつ一気読みのサンプルコード：

```java:ReadLines.java
import java.nio.charset.*;
import java.nio.file.*;

public class ReadLines {
    public static void main(String[] args) throws Exception {
        for (String line : Files.readAllLines(Paths.get(args[0]), Charset.forName(args[1]))) {
            System.out.println(line);
        }
    }
}
```

試しにCR, CRLF, LF で改行したテキストファイルを読み込ませたところ、混在しててもちゃんと一行ずつに分割してくれました。