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)
※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だとどう書くんだっけ?」という時に本記事はぴったりかもです。
basename,dirname相当
最初の紹介で申し訳ないですが、これについてはJava6以前の java.io.File
クラスでも提供されてたようで・・・自分が知らないだけだったようです(汗)。
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
- パスからファイル名を取得するには (basename) | 入出力・ファイル操作 | プログラミング言語の比較 | hydroculのメモ
- Java file dirname and basename | Xinotes
- file io - Getting the directory name in java - Stack Overflow
ファイル読み書きのショートカット
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)
一行ずつ一気読みのサンプルコード:
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 で改行したテキストファイルを読み込ませたところ、混在しててもちゃんと一行ずつに分割してくれました。