トップJava > ファイルの操作

ファイルの操作

テキストファイルの行単位の読み込み

    try (BufferedReader br = new BufferedReader(new FileReader(file))) {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

テキストファイルの書き込み

テキストの書き込みは FileWriter あるいは BufferedWriter を使う。

パイプ接続で前段の標準出力を受ける場合は System.in を使う。

  try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        FileWriter fw = new FileWriter(new File(pathout)) ) {
    String line;
    while ((line = br.readLine()) != null) {
        fw.write(line);
    }
 } catch (Exception e) {
    e.printStackTrace();
 }

バイナリファイルの読み込みと書き込み[2]

    try (FileInputStream fis = new FileInputStream("input.jpg");
            FileOutputStream fos = new FileOutputStream("output.jpg")) {
        int data;
        while ((data = fis.read()) != -1) { // read()は読み取ったバイト数をint型で返し、
            fos.write(data);                // ファイルの終わりに達した場合は-1を返す
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

バイナリファイルの読み込みの高速化

下の例では、BufferedInputStream を介在させることにより、 合計約1GBの読み込みが100倍以上高速化され実行時間は 0.3分となった。

読み込み終了は EOFException での検出となるため、catch は二つに分ける必要がある。

  try (DataInputStream dis = new DataInputStream(
         new BufferedInputStream(
          new FileInputStream("c:/osm/" + name + "_idlonlat.dat")))) {
      while (true) {
          long nid = dis.readLong();
          int lon = dis.readInt();
          int lat = dis.readInt();
          put(nid, lon, lat);
      }
  } catch (EOFException e) {
      System.out.println(""); // ファイル読み込み完了
  } catch (Exception e) {
      e.printStackTrace();
  }

バイナリファイルの書き込みの高速化

  try (DataOutputStream dos = new DataOutputStream(
          new BufferedOutputStream(new FileOutputStream(path)))) {
      for (int i = 0; i < cntNodes; i++) {
           dos.writeInt(sids[i]);
           dos.writeInt(lons[i]);
           dos.writeInt(lats[i]);
      }
 } catch (Exception e) {
     e.printStackTrace();
 }

ファイルサイズの取得[3]

  long fileSize = Files.size(Paths.get("c:/osm/japan.osm"));

ファイル、ディレクトリの存在を調べる

  boolean fExists = Files.exists(Paths.get("c:/osm/japan.osm"));

ディレクトリを作成する

パスの途中にあるディレクトリもなければ作成される。

  Files.createDirectories(Paths.get("c:/map/japan-high7/113"));

ファイルの更新日時を取得する

  FileTime fileTime = Files.getLastModifiedTime(Paths.get("c:/osm/japan.osm"));

テキストファイルを分割する

テキストファイルを行単位で読み込み、ASCII文字が中心の場合、約1GBの複数のファイルに分割するプログラムを 下に示す。

行単位で読み込み、 改行コードを除いた文字数が 1000,000,000 以上になったとき、出力ファイルを更新している。 OSM(OpenStreetMap)の日本地図領域ファイルの場合、若干、日本語など一文字が2バイト以上になるものがあることと、 改行コードの影響で、個々のファイルサイズは最後の端数ファイルを除き、1000,000,000バイトをやや上回る。

約40GBファイルの分割にパソコンで2時間ほど要した。 バイナリデータの読み込みとして、1回あたりの読み込みサイズを大きくして、 改行コードの検出をスキャンで行えば、高速化が図れる。

ファイルの切れ目に拘らず、1GB単位での分割の場合にもはるかに高速分割ができる。

/*
javac -d ./class splitter/*.java

java -Dfile.encoding=UTF-8 -classpath ./class Splitter japan
*/
import java.io.*;
import java.nio.*;
import java.nio.file.*;
import java.util.*;

public class Splitter {
    static void splitFile(String name) throws IOException {
        String dirDst = "d:/osms/" + name + "/";
        String pathSrc = "c:/map/data/" + name + ".osm";

        // 元ファイルを1行ずつ読み込む
        try (BufferedReader bufferedReader = Files.newBufferedReader(Paths.get(pathSrc))) {
            int length = 0;
            String str;
            boolean run = true;
            for (int splitCount = 0; run; splitCount++) {
                String pathDst = dirDst + splitCount + ".osm";
                System.out.printf("%s\n", pathDst);
                try (BufferedWriter bufferedWriter = Files.newBufferedWriter(Paths.get(pathDst))) {
                    while (length < 1000000000) {
                        if ((str = bufferedReader.readLine()) != null) {
                            bufferedWriter.write(str);
                            bufferedWriter.newLine();
                            length += str.length();
                        } else {
                            run = false;// 読み込む行がなくなった場合、処理を終了する
                            break;
                        }
                    }
                    length = 0;
                }
            }
        }
    }


    public static void main(String[] args) throws Exception {
        long start = System.currentTimeMillis();
        splitFile(args[0]);
        long elapsed = System.currentTimeMillis() - start;
        System.out.printf("SPlitter実行時間 = %.2f分\n", elapsed/1000.0/60);
    }

}

リファレンス

[1] Java で大きなテキストファイルを一行ずつ読み込む方法
[2] 【Java】バイナリファイルの読み込みと出力
[3] ファイルのサイズを取得する(Files.size)
[4] クラスFiles
[5] 【Java】ファイルを行単位で指定個数に分割する方法