文字セット
文字とバイナリデータ間の対応関係を定義したマッピングテーブルです。
バイトはコンピュータの基本的な記憶単位であり、文字は特定の規則とエンコーディングによって構成されたテキストです。
一般的な文字セット
- ASCII文字セット
基本的な文字エンコーディング標準で、128文字を含み、英字、数字、一部の記号のみを扱います。
1バイトで1文字を表現します。
すべての文字セットはASCII文字セットに互換性があります。 - GBK国家規格コード
ASCII文字セットを拡張したもので、漢字は2バイトで表現し、英数字は1バイトで表現します。
約2万以上の文字を表現可能で、中国語の多くの文字に対応していますが、世界的には利用できません。 - Unicodeグローバル文字エンコーディング標準
すべての既知の文字を含み、各種言語の文字や記号、グラフィックをサポートします。
文字範囲に応じて異なるバイト長を使用して効率的な保存と転送を実現します。
UTF-8エンコーディングでは、漢字は3バイトで表現し、英数字は1バイトで表現します。
エンコーディングとデコーディング
- エンコーディング: 文字を指定された文字セットでバイトに変換すること
- デコーディング: バイトを指定された文字セットで文字に戻すこと
Stringクラスはエンコーディングとデコーディングのためのメソッドを提供しています。
コード例:
byte[] encodedData = textContent.getBytes("文字セット名"); // エンコーディング
String decodedText = new String(encodedData, "文字セット名"); // デコーディング
エンコーディングとデコーディングの文字セットは一致させる必要があります。一致しない場合、文字化けが発生します。ただし、英数字は通常文字化けしません。
文字ストリーム
文字ストリーム = バイトストリーム + エンコーディングテーブル
UTF-8やGBKなどのエンコーディングでは、中国語は2〜3バイトが必要なため、バイトストリームで読み込むと半角の漢字が読み込まれ、文字化けが発生します。文字ストリームを使用することでこの問題を解決できます。
文字ストリームの分類
- 文字入力ストリーム: 最上位の抽象クラスはReaderで、FileReaderなどのサブクラスが使用されます。
- 文字出力ストリーム: 最上位の抽象クラスはWriterで、FileWriterなどのサブクラスが使用されます。
文字入力ストリーム
コンストラクタ:
public FileReader(File targetFile) // 文字入力ストリームとファイルを接続
public FileReader(String filePath) // 文字入力ストリームとファイルパスを接続
メソッド:
public int readCharacter() // 1文字読み込み、データがない場合は-1を返す
public int readCharacters(char[] buffer) // 文字配列で読み込み、読み込んだ文字数を返す、データがない場合は-1を返す
文字出力ストリーム
コンストラクタ:
public FileWriter(File targetFile) // 出力ストリームとファイルを接続
public FileWriter(String filePath) // 出力ストリームとファイルパスを接続
public FileWriter(File targetFile, boolean appendMode) // ファイルに追記モードで接続
public FileWriter(String filePath, boolean appendMode) // ファイルパスに追記モードで接続
メソッド:
void writeSingleChar(int character) // 単一文字を書き込む
void writeString(String content) // 文字列を書き込む
void writeStringPart(String content, int start, int length) // 文字列の一部を書き込む
void writeCharArray(char[] array) // 文字配列を書き込む
void writeCharArrayPart(char[] array, int start, int length) // 文字配列の一部を書き込む
void flushBuffer() // バッファをフラッシュし、メモリのデータをディスクに反映
void closeStream() // ストリームを閉じる(自動的にフラッシュも実行)
バッファーストリーム
元のストリームをラップして、読み書き性能を向上させます。内部に8KBのバッファプールを持ち、データをメモリバッファに一時保存してから一括してディスクやネットワークに書き込むことで、実際のI/O回数を削減します。
バッファーストリームの利点
- I/O操作回数を削減
- 読み書き効率を向上
- 大量データ処理時のパフォーマンス向上
flush()メソッドについて
- バイトストリームはファイルがバイト形式であるため、変換なしで直接ファイルに書き込めます。バイトストリームはバッファを持たないため、バッファーストリームによる改善効果が大きく、flush()メソッドは実装されていません。
- 文字ストリームはメモリ内で変換が必要なため、flush()メソッドで手動でデータをディスクに書き込む必要があります。文字ストリームは内部バッファを持つため、バッファーストリームとの差異は小さくなります。
- バッファーストリームは内部に8KBの配列を作成し、配列が満杯になるとディスクに保存することで、メモリとディスクのやり取りを削減し効率を向上させます。
IOストリーム体系
バイトストリームのラップ構築:
public BufferedInputStream(InputStream sourceStream) // 基本バイト入力ストリームをバッファ付きに変換
public BufferedOutputStream(OutputStream sourceStream) // 基本バイト出力ストリームをバッファ付きに変換
文字ストリームのラップ構築:
public BufferedReader(Reader sourceReader) // 基本文字入力ストリームをバッファ付きに変換
特別メソッド:
public String readLine() // 1行読み込み(改行文字を含まない)、データがない場合はnullを返す
public BufferedWriter(Writer sourceWriter) // 基本文字出力ストリームをバッファ付きに変換
特別メソッド:
public void writeNewLine() // システムに応じた改行文字を出力
変換ストリーム
Java IOライブラリの特別なストリームで、バイトストリームを文字ストリームに変換する際に使用されます。例えば、ネットワークプログラミングで日本語を含むバイトデータを送信する際に変換ストリームが必要です。
InputStreamReaderとOutputStreamWriterの2つのクラスを提供し、変換ストリームは文字ストリームの一種であるため、すべての文字ストリーム機能を利用できます。
入力ストリーム変換コンストラクタ:
InputStreamReader(InputStream inputSource) // デフォルト文字セットを使用してバイト入力を文字入力に変換
InputStreamReader(InputStream inputSource, String encodingName) // 指定された文字セットでバイト入力を文字入力に変換
出力ストリーム変換コンストラクタ:
OutputStreamWriter(OutputStream outputTarget) // デフォルト文字セットを使用してバイト出力を文字出力に変換
OutputStreamWriter(OutputStream outputTarget, String encodingName) // 指定された文字セットでバイト出力を文字出力に変換
直列化ストリーム操作
オブジェクトをバイトストリームに変換してネットワーク伝送やファイル保存を行う処理です。オブジェクトの状態を保存して必要時に再生成できるようにします。
ObjectOutputStreamクラス
オブジェクトをバイトストリームに直列化します。
コンストラクタ:
ObjectOutputStream(OutputStream outputDestination) // 直列化データを指定された出力ストリームに書き込み
オブジェクト書き出し:
public void writeSerializedObject(Object targetObject) // 指定されたオブジェクトを直列化して出力ストリームに書き込み
ObjectInputStreamクラス
バイトストリームをオブジェクトに逆直列化します。
コンストラクタ:
ObjectInputStream(InputStream inputSource) // 指定された入力ストリームから逆直列化データを読み込み
オブジェクト読み込み:
ファイル終端まで読み込むとEOF例外が発生するため、try-catchで捕捉する必要があります。
public Object readDeserializedObject() // 入力ストリームからバイトを読み込んでオブジェクトに逆直列化
注意事項:
- 書き込み対象のオブジェクトはSerializableインターフェースを実装する必要があります。このインターフェースはマーカーインターフェースであり、メソッドの実装は不要ですが、JVMに対してこのクラスのオブジェクトが直列化可能であることを示します。実装しない場合、NotSerializableExceptionが発生します。
- transientキーワードは変数に修飾するために使用されます。transientで修飾された変数は直列化されません。つまり、オブジェクトがファイルに書き込まれたりネットワーク経由で転送される際、その変数の値は保存または転送されません。
- オブジェクトをファイルに直列化した後、逆直列化前にクラスを変更すると例外が発生します。直列化時にクラス情報に基づいてシリアルバージョンUIDが生成され、クラス情報を変更すると新しいUIDが生成されるため、InvalidClassExceptionが発生します。
解決策:
固定的なserialVersionUIDを設定することで、クラス構造が変更された場合でもバージョンの一貫性を保つことができます。
private static final long serialVersionUID = 123456789L;
直列化の利用方法
オブジェクトをコンテナに事前に格納し、必要時にコンテナオブジェクトを直列化することで効率化できます。保存時は1回のコンテナ保存で済み、読み込み時も1回の読み込みで完了し、ループ判定を回避できます。
IOフレームワーク commons-io
Apacheのオープンソースプロジェクトで、サードパーティのツールパッケージです。Javaコードを簡略化するための一般的なI/O関連ユーティリティクラスを提供し、公式コアライブラリとは独立しています。
主な機能:
- ファイル操作:コピー、削除、移動など
- ファイルフィルタリング:ファイル名や拡張子に基づくフィルタリング
- 文字セット操作:バイト配列から文字列への変換など
利用方法:
- JARファイルをダウンロードしてインポート
- libsフォルダを作成
- JARファイルをフォルダにコピー
- JARファイル上で右クリックして「Add as Library」を選択
公式APIドキュメント:https://commons.apache.org/proper/commons-io/javadocs/api-release/