0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Java I/O(入出力)操作

Last updated at Posted at 2024-06-30

Java 11 Gold 取得に向けた学習記録

IO(入出力)操作

java.ioパッケージ

File

File(ファイル)
File file = new File("a.txt");
File(ディレクトリ)
File dir = new File("a");

// カレントディレクトリ
File dir = new File(".");

exists()

exists()
File file = new File("a.txt");
System.out.println(file.exists());
// >> false

createNewFile()

createNewFile()
try {
    File file = new File("a.txt");
    file.createNewFile();
} catch (IOException e) {
}    

mkdirs()

mkdirs()
File dir = new File("a");
dir.mkdirs();

isDirectory()

isDirectory()
File dir = new File("a");

System.out.println(dir.isDirectory());
// >> false

dir.mkdirs();

System.out.println(dir.isDirectory());
// >> true

getAbsoluteFile()

Fileオブジェクトは本質的には「パス」を扱うクラスなので、実際のファイルの有無は関係ない

getAbsoluteFile()
File file = new File("a.txt");

System.out.println(file.getAbsoluteFile());
// >> /Users/asahinakei/IdeaProjects/Qiita/a.txt

list()

返される型がString[]

list()
File dir = new File(".");
String[] files = dir.list(); // String[]

for (String file : files) {
    System.out.println(file);
}
// >> a
// >> gradle
// >> gradlew
// >> build.gradle
// >> .gradle
// >> build
// >> gradlew.bat
// >> settings.gradle
// >> .idea
// >> src

listFiles()

返される型がFile[]

listFiles()
File dir = new File(".");
File[] files = dir.listFiles(); // File[]

for (File file : files) {
    System.out.println(file.getName());
}
// >> a
// >> gradle
// >> gradlew
// >> build.gradle
// >> .gradle
// >> build
// >> gradlew.bat
// >> settings.gradle
// >> .idea
// >> src

java.nio.fileパッケージ

nio = New IO

java.io.Fileクラスには、UNIX系OSにおけるシンボリックリンクやファイル属性が適切に扱えないなどの課題があったことから、Java 7ではjava.nio.fileパッケージが導入された。

java.nio.fileパッケージに含まれるPathインターフェースとFilesクラスを利用することで、java.io.Fileクラスが適切に扱うことのできなかったファイル属性を簡単に扱ったり、ファイル削除、移動などの高度な操作を簡単に行うことができる。

  • Path : インターフェース
  • Paths : Pathインターフェースの実装クラスを生成する factory メソッドが定義されたクラス
  • Files : java.io.Fileクラスの改良版

Path / Paths

Pathインターフェースの実装を取得する
Path path = Paths.get("a.txt");

パスを可変長引数によって表現することができる

パスの表現
Path path = Paths.get("aDir", "bDir", "c.txt");

PathFileへ変換することができる。

Path ▶️ File
Path path = Paths.get("a.txt");

File file = path.toFile();

Pathインターフェースの実装は、java.io.Fileクラスからも取得することができる。

File ▶️ Path
File file = new File("a.txt");

Path path = file.toPath();

Files

Filesクラスは多くの場合、Filesクラスのstaticメソッドに対してPathインターフェースの実装を渡すと言う形で利用する。

exists()

exists()
Path path = Paths.get("a.txt");
System.out.println(Files.exists(path));
// >> false

createFile()

作成しようとしているファイルがすでに存在する場合、FileAlreadyExistsExceptionが発生する。

createFile()
try {
    Path path = Paths.get("a.txt");
    Files.createFile(path);
} catch (IOException e) {
}

delete()

削除しようとしているファイルが存在しない場合、NoSuchFileExceptionが発生する。

delete()
try {
    Path path = Paths.get("a.txt");
    Files.delete(path);
} catch (IOException e) {
}

getPosixFilePermissions()

POSIX = Portable Operation System Interface for UNIX

getPosixFilePermissions()
try {
    Path path = Paths.get("a.txt");
    System.out.println(Files.getPosixFilePermissions(path));
} catch (IOException e) {
}

// >> [OWNER_READ, OTHERS_READ, OWNER_WRITE, GROUP_READ]

createDirectory()

親ディレクトリの存在を前提にしているため、ディレクトリaが存在しない場合、NoSuchFileExceptionが発生する。

また作成対象のディレクトリがまだ存在していないことを前提としているため、cディレクトリがすでに存在した場合にも、FileAlreadyExistsExceptionが発生する。

createDirectory()
try {
    Path path = Paths.get("a", "b", "c");
    Files.createDirectory(path);
} catch (IOException e) {
}

createDirectories()

親ディレクトリ、作成対象ディレクトリの存在を前提としていないため、指定パスの中で存在しないディレクトリがあったとしても、親ディレクトリも含めて作成することができる。

createDirectories()
try {
    Path path = Paths.get("a", "b", "c");
    Files.createDirectories(path);
} catch (IOException e) {
}

resolve()

Pathを連結することで、複数のファイルが同じディレクトリにある場合などに共通パス部分とファイル固有のパス部分を分割することができる。

resolve()
Path dir = Paths.get("a", "b", "c");

Path file1 = dir.resolve(Paths.get("d.txt"));
Path file2 = dir.resolve(Paths.get("e.txt"));

copy()

copy()
try {
    Path original = Paths.get("a.txt");
    Path copy = Paths.get("a_copy.txt");
    Files.copy(original, copy);
} catch (IOException e) {
}

move()

move()
try {
    Path source = Paths.get("a" + "b" + "c.txt");
    Path destination = Paths.get("a" + "c.txt");
    Files.move(source, destination);
} catch (IOException e) {
}

walk()

ファイルツリーを再帰的にトラバース(traverse=横切る)するための便利メソッド。

walk()
Path dir = Paths.get("a");
try (Stream<Path> stream = Files.walk(dir)) {
    stream.forEach((path) -> {
        System.out.println(path.getFileName());
    });
} catch (IOException e) {
}

walkFileTree()

ファイルツリーを再帰的にトラバースする際に、トラバースを細かく制御することができる高機能なメソッド。

FileVisitorインターフェースの実装を渡す必要がある。

FileVisitor

FileVisitorインターフェースは、walkFileTree()への引数として利用され、以下の4つの抽象メソッドが定義されている。それぞれのメソッドは各々決められたタイミングでFilesによって実行される。

事前にコールバック関数を描くメソッドに仕込んでおき、コールバックのセットをwalkFileTree()へ渡すイメージ。

FileVisitor
// 一部省略しています
public interface FileVisitor<T> {
    // ディレクトリに入るとき
    FileVisitResult preVisitDirectory(T dir, BasicFileAttributes attrs);

    // ファイルに到達したとき
    FileVisitResult visitFile(T file, BasicFileAttributes attrs);

    // ディレクトリから出るとき
    FileVisitResult postVisitDirectory(T dir, IOException exc);

    // 処理に失敗したとき
    FileVisitResult visitFileFailed(T file, IOException exc);
}

FileVisitResult

処理終了後の動作はenumのFileVisitResultを返すことで制御ができる。

FileVisitResult
public enum FileVisitResult {
    // トラバースを続行する
    CONTINUE,

    // トラバースを終了する
    TERMINATE,
    
    // このファイルまたはディレクトリの兄弟を visit せずにトラバースを続行する
    SKIP_SIBLINGS,
    
    // このディレクトリ内を visit せずにトラバースを続行する
    SKIP_SUBTREE,
}

SimpleFileVisitor

FileVisitorインターフェースには4つのメソッド定義があるため、本来は4つとも実装しなければならないがSimpleFileVisitorクラスを使用することで、その手間を省くことができる。

SimpleFileVisitorクラスはすでにFileVisitorインターフェースを実装しているため、自分がカスタマイズしたいメソッドのみオーバーライドすることができる。

try {
    Path dir = Paths.get("a");
    Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
        // visitFile()だけをオーバーライドする
        @Override
        public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
            System.out.println("hello world");
            return FileVisitResult.CONTINUE;
        }
    });
} catch (IOException e) {
}

ストリーム

java.ioパッケージから提供されるクラスは、入出力を「文字」として扱うのか「バイナリデータ(010111011みたいな2進数表現)」として扱うのかによって使い分ける。

ここでのInputStreamOutputStreamはJava 1(JDK 1.0)のもので、Java 8 から導入されたストリームAPIとは別のものであることに注意する。

またAutoCloseableインターフェースやCloseableインターフェースを実装したクラスをインスタンス化する場合、try-finally構文で明示的にclose()するか、もしくはtry-with-resource構文で自動的にclose()する必要があるため注意する。

AutoCloseableCloseableインターフェースの実装クラスをインスタンス化する場合、クローズ処理が必要

文字データを扱うストリーム

FileReader

FileReader
// close が必要
try (FileReader reader = new FileReader("a.txt")) {

} catch (IOException e) {
}

read()

read() はファイルの終端に到達すると -1 を返す

read()
try (FileReader reader = new FileReader("a.txt")) {
    int i = 0;
    // read() はファイルの終端に到達すると -1 を返す
    while ((i = reader.read()) != -1) {
        char c = (char) i;
        System.out.print(c);
    }
} catch (IOException e) {
}

// >> hello world
// >> hello world
// >> hello world
// >> hello world
// >> hello world

BufferedReader

前述のread()では、1文字づつファイルへのアクセスを行っている。一般的に、IO(Input/Output)処理はプログラムの実行と比較して大変重たい処理になるため、バッファを使用してファイルシステムへのアクセスを最小限に抑える方が望ましい。

バッファを使用することで入出力内容を一時的にバッファに貯めておくことができるため、ファイルシステムへのアクセス回数が大幅に減りパフォーマンスが向上する。

java.ioパッケージの場合

BufferedReader (java.io.File)
// close が必要
try (FileReader fileReader = new FileReader("a.txt");
     // 生成時に FileReader を渡す必要がある
     BufferedReader bufferReader = new BufferedReader(fileReader)) {
    
} catch (IOException e) {
}

BufferedReaderクラスは、コンストラクがReaderクラスを受け取るようになっている。また、BufferedReaderクラスはReaderクラスを継承しているため、どちらもReader型として扱うことができる。コンストラクタが受け取ったReaderクラスは内部で使用されていて、一部のメソッドがオーバーライドされることで、Readerクラスの機能が拡張されている。このようにオブジェクトを別のオブジェクトでラップすることによって機能を拡張するデザインパターンをDecoratorパターンと言う。

java.nio.fileパッケージの場合

BufferedReader (java.nio.file.Files)
Path path = Paths.get("a.txt");
try (BufferedReader bufferReader = Files.newBufferedReader(path)) {
    
} catch (IOException e) {
}

readLine()

readLine() はファイルの終端に到達すると null を返す

readLine()
try (FileReader fileReader = new FileReader("a.txt");
     BufferedReader bufferReader = new BufferedReader(fileReader)) {
    String line = null;
    // readLine() はファイルの終端に到達すると null を返す
    while ((line = bufferReader.readLine()) != null) {
        System.out.println(line);
    }
} catch (IOException e) {
}

// >> hello world
// >> hello world
// >> hello world
// >> hello world
// >> hello world

FileWriter

FileWriterをインスタンス化する際、指定ファイルが存在しなければその時点でファイルが作成される。

FileWriter
// ファイルが存在しない場合、この時点で新規作成される(close が必要)
try (FileWriter writer = new FileWriter("a.txt")) {
    
} catch (IOException e) {
}

write()

write()はデフォルトでは上書きモードで書き込みを行う。

write() 上書きモード
// デフォルトでは上書きモード
try (FileWriter writer = new FileWriter("a.txt")) {
    writer.write("hello world");
} catch (IOException e) {
}

追記モードで書き込みを行いたい場合は、FileWriterのインスタンス化の際に設定を行う。

write() 追記モード
// 追記モード
try (FileWriter writer = new FileWriter("a.txt", true)) {
    writer.write("hello world");
} catch (IOException e) {
}

BufferedWriter

java.ioパッケージの場合

BufferedWriter (java.io)
try (FileWriter fileWriter = new FileWriter("a.txt");
     // 生成時に FileWriter を渡す必要がある(close が必要)
     BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
    
} catch (IOException e) {
}

java.nio.fileパッケージの場合

BufferedWriter (java.nio.file) 上書きモード
Path path = Paths.get("a.txt");
try (BufferedWriter bufferWriter = Files.newBufferedWriter(path)) {
    
} catch (IOException e) {
}
BufferedWriter (java.nio.file) 追記モード
Path path = Paths.get("a.txt");
try (BufferedWriter bufferWriter = Files.newBufferedWriter(path, StandardOpenOption.APPEND)) {

} catch (IOException e) {
}

write()

write()
try (FileWriter fileWriter = new FileWriter("a.txt");
     BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {
    for (int i = 0; i < 5; i++) {
        bufferedWriter.write("hello world");
        bufferedWriter.newLine(); // 改行
    }
    // メモリ(上のバッファ)とファイルが同期される
    bufferedWriter.flush();
} catch (IOException e) {
}

バイナリデータを扱うストリーム

FileInputStream / BufferedInputStream

FileInputStream / BufferedInputStream
// close が必要
try (FileInputStream fileInputStream = new FileInputStream("a.png");
     // 生成時に fileInputStream を渡す必要がある
     BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {
    
} catch (IOException e) {
}

FileOutputStream / BufferedOutputStream

FileOutputStream / BufferedOutputStream
// close が必要
try (FileOutputStream fileOutputStream = new FileOutputStream("a.png");
     // 生成時に fileOutputStream を渡す必要がある
     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {
    
} catch (IOException e) {
}

コンソール

コンソールからの入力を受け付けたい場合、java.lang.Systemクラスを使用する。ただし、この方法は後述するjava.io.Consoleクラスによってさらに簡略化することができる。

Systemを使う方法

Systemクラスを利用する場合、バイナリデータを扱うストリームを文字を扱うストリームに変換する処理が必要になる。

この変換はブリッジと呼ばれ、java.io.InputStreamReaderクラスによって行うことができる。InputStreamReaderクラスはバイナリデータ(InputStream)を文字エンコーディングを使用して文字データ(Reader)にデコードすることができる。

この辺りが非常にややこしいので再掲。

InputStreamReader
// 通常  InputStream ▶️ BufferedInputStream
// 変換時 InputStream ▶️ 【InputStreamReader】 ▶️ BufferedReader
try (InputStream inputStream = System.in;
     InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
     BufferedReader bufferReader = new BufferedReader(inputStreamReader)) {
    String input = bufferReader.readLine();
    System.out.println(input);
} catch (IOException e) {
}

// コンソールが入力を待つ
// 「hello world」とコンソールへ入力してEnter
// >> hello world

Consoleを使う方法

Consoleはコンソールからの入出力をサポートするクラスなので、IDEのツールからプログラムを実行した場合にはSystem.console()nullを返すことがあるため注意。

Console
Console console = System.console();
if (console != null) {
    String input = console.readLine();
    System.out.println(input);
}

// コンソールが入力を待つ
// 「hello world」とコンソールへ入力してEnter
// >> hello world

Scannerを使う場合

標準入力(System.in)からのデータの読み取りをサポートするクラス。

Scanner
Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();
System.out.println(input);

// コンソールが入力を待つ
// 「hello world」とコンソールへ入力してEnter
// >> hello world

FileReader vs InputStream

テキストファイルを読み込む場合、FileReader を使用すべきなのか、InputStream を使用すべきなのか。

FileReader
記述が簡単な一方で、プラットフォームのデフォルトエンコーディングを使用するため、特定のエンコーディングを指定する場面に不向き。

InputStream
エンコーディングを指定して読み取りを行うことができる。

参考

徹底攻略Java SE 11 Gold問題集[1Z0-816]対応

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?