技術とか戦略とか

SIerで証券レガシーシステムを8年いじってからSESに転職した普通の文系SEによる技術ブログ。

java:ファイルを1バイトずつ/1文字ずつ読み書きする

ファイルの中身を前から順番に解釈しながら読み込む場合、ファイルを1バイトずつ/1文字ずつ読み込むと処理がしやすいです。
ここでは、読み込みと書み込みのjavaのサンプルコードを示します。
 
1バイトずつ読み込んだ方が汎用性は高いのですが、1バイトずつ読み込む場合はマルチバイト文字も1バイトずつ分割しながら読み込むので、マルチバイト文字の内容を解釈する場合は1文字ずつ読み込んだ方が楽です。
ただし、1文字ずつ読み込む場合は以下のことに注意する必要があります。
・文字エンコーディングを指定する必要がある
javaの内部(メモリ)では"UTF-16"として文字列が保持される
 
【サンプルコード】
・FileReadWriteMain.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.ByteBuffer;

public class FileReadWriteMain {

  public static void main(String args) {

    // 1バイトずつ読込(バイナリファイルとして処理)
    System.out.println("[1バイトずつ読込]");
    FileInputStream fis = null;
    FileOutputStream fos = null;
    try {
      fis = new FileInputStream("C:\\tmp\\test.txt");
      fos = new FileOutputStream("C:\\tmp\\test_b.txt");
      int data;
      while ((data = fis.read()) != -1){
        // 表示のため、読み込んだ1バイトのデータを変換
        // int → byte配列 → 文字列
        // byte配列は4バイト固定、文字列にパディングされたnul文字は削除
        byte bytes = ByteBuffer.allocate(4).putInt(data).array();
        String str = new String(bytes, "MS932");
        str = str.replace("\u0000", "");
        // 表示のため、読み込んだ1バイトのデータを変換
        // 10進数 → 16進数
        String hex = Integer.toHexString(data);
        if (hex.length() == 1) {
          hex = "0" + hex;
        }
        // 読み込んだ1バイトのデータを表示
        System.out.println
          ("読み込んだデータ:" + str + "(0x" + hex + ")");
        // ファイルに書き込み
        fos.write(data);
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        fis.close();
        fos.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

    // 1文字ずつ読込(テキストファイルとして処理)
    System.out.println();
    System.out.println("[1文字ずつ読込]");
    InputStreamReader isr = null;
    OutputStreamWriter osw = null;
    try {
      isr = new InputStreamReader(
          new FileInputStream("C:\\tmp\\test.txt"), "MS932");
      osw = new OutputStreamWriter(
          new FileOutputStream("C:\\tmp\\test_t.txt"), "MS932");
      int data;
      while ( (data = isr.read() ) != -1){
        // 表示のため、読み込んだ1文字のデータを変換
        // int → byte配列 → 文字列
        // javaでは内部的な文字エンコーディングに変換されることに注意
        // (内部的にはUTF-16を使用)
        byte[] bytes = ByteBuffer.allocate(4).putInt(data).array();
        String str = new String(bytes, "UTF-16");
        str = str.replace("\u0000", "");
        // 表示のため、読み込んだ1文字のデータを変換
        // 10進数 → 16進数
        String hex = Integer.toHexString(data);
        if (hex.length() == 1) {
          hex = "0" + hex;
        }
        // 読み込んだ1文字のデータを表示
        System.out.println
          ("読み込んだデータ:" + str + "(0x" + hex + ")");
        // ファイルに書き込み
        osw.write(data);
      }
    } catch (IOException e) {
      e.printStackTrace();
    } finally {
      try {
        isr.close();
        osw.close();
      } catch (IOException e) {
        e.printStackTrace();
      }
    }

  }
}

 
【入力ファイル】
・test.txt(文字エンコーディングは"MS932"(Shift-JIS))
Hello
わーるど!
 
【実行結果】
・コンソール
[1バイトずつ読込]
読み込んだデータ:H(0x48)
読み込んだデータ:e(0x65)
読み込んだデータ:l(0x6c)
読み込んだデータ:l(0x6c)
読み込んだデータ:o(0x6f)
読み込んだデータ:
(0x0d)
読み込んだデータ:
(0x0a)
読み込んだデータ:�(0x82)
読み込んだデータ:�(0xed)
読み込んだデータ:�(0x81)
読み込んだデータ:[(0x5b)
読み込んだデータ:�(0x82)
読み込んだデータ:�(0xe9)
読み込んだデータ:�(0x82)
読み込んだデータ:ヌ(0xc7)
読み込んだデータ:�(0x81)
読み込んだデータ:I(0x49)

[1文字ずつ読込]
読み込んだデータ:H(0x48)
読み込んだデータ:e(0x65)
読み込んだデータ:l(0x6c)
読み込んだデータ:l(0x6c)
読み込んだデータ:o(0x6f)
読み込んだデータ:
(0x0d)
読み込んだデータ:
(0x0a)
読み込んだデータ:わ(0x308f)
読み込んだデータ:ー(0x30fc)
読み込んだデータ:る(0x308b)
読み込んだデータ:ど(0x3069)
読み込んだデータ:!(0xff01)
 
【出力ファイル】
下記の通り差異無し。

f:id:akira2kun:20210717184443j:plain