Java | Java 標準ライブラリ:文字コード(Charset)

Java Java
スポンサーリンク

文字コードと Charset を直感でつかむ

まず前提から整理します。

コンピュータの中では、文字は「バイト列(0と1の並び)」として保存・送信されます。
一方、Java の String は「文字そのもの(Unicode)」として扱われます。

この「文字 ↔ バイト列」を変換するときに
「どのルールで変換するか?」を決めるのが文字コード(文字セット/エンコーディング)で、
それを Java で表現したクラスが java.nio.charset.Charset です。

イメージとしては、

文字(Java の String
↓ エンコード(文字 → バイト)
バイト列(ファイル・ネットワーク)
↑ デコード(バイト → 文字)
文字(Java の String

この「エンコード・デコードで使う変換ルール」が Charset です。


Java と文字コードの関係(ここはしっかり)

Java の中では Unicode(UTF-16)

Java の String 型は、内部的には「UTF-16 をベースにした Unicode」で文字を扱っています。
つまり、プログラムの中で String として持っているうちは、
Shift_JIS だとか UTF-8 だとかは意識しません。

問題になるのは、

ファイルに書く/読む
ネットワークで送る/受け取る
バイト配列と文字列を相互変換する

こういう、「外部の世界」とやりとりするときです。

このときに「どの文字コードでエンコード/デコードするのか」を
明示的に指定しないと、「文字化け」や「読めないデータ」の原因になります。


Charset クラスの基本(StandardCharsets と forName)

よく使う Charset の取り方

Charset は主に「どの文字コードを使うか」を指定するために使います。

代表的なのが、StandardCharsets 経由の取り方です。

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

Charset utf8 = StandardCharsets.UTF_8;
Charset sjis = Charset.forName("Shift_JIS");

System.out.println(utf8);  // UTF-8
System.out.println(sjis);  // Shift_JIS
Java

StandardCharsets.UTF_8 のように書くと、
コンパイル時に存在チェックもされますし、書き間違えにくいのでおすすめです。

Charset.forName("Shift_JIS") のように文字列で指定すると、
名前を間違えたときに UnsupportedCharsetException が出ます。
どうしても文字列指定が必要なとき以外は、StandardCharsets を優先すると安全です。


文字列とバイト配列の変換(エンコード/デコード)

getBytes で「文字列 → バイト列」

String#getBytes(Charset) を使うと、指定した文字コードで「文字列 → バイト列」に変換できます。

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

String text = "abc♪";

byte[] utf8Bytes = text.getBytes(StandardCharsets.UTF_8);
byte[] sjisBytes = text.getBytes(StandardCharsets.SHIFT_JIS);

System.out.println(Arrays.toString(utf8Bytes));
System.out.println(Arrays.toString(sjisBytes));
Java

同じ "abc♪" でも、UTF-8 と Shift_JIS ではバイト列が違います。
実際、UTF-8 では abc の部分で 6 バイト(ASCII 3 + マルチバイト 3)など、
Shift_JIS では別のバイトパターンになります。

ここで大事なのは、

「文字列の見た目は同じでも、文字コードが違えばバイト列は変わる」

という感覚です。

new String(byte[], Charset) で「バイト列 → 文字列」

逆に、バイト列から文字列に戻すときは new String(bytes, charset) を使います。

byte[] bytes = ...;  // どこかから来たバイト列(UTF-8 だと分かっているとする)

String text = new String(bytes, StandardCharsets.UTF_8);
System.out.println(text);
Java

ここで重要なのは、

エンコードに使った Charset と、デコードに使う Charset を揃えること

です。
UTF-8 でエンコードしたデータを Shift_JIS として解釈したりすると、即文字化けします。


「デフォルト文字コード」に頼る危険(ここはかなり重要)

getBytes() や new String(bytes) の引数なし版は危ない

Java では、引数なしの getBytes()new String(bytes) も書けますが、
これらは「プラットフォームのデフォルト文字コード」を使います。

String text = "こんにちは";
byte[] bytes = text.getBytes();        // デフォルト文字コードでエンコード
String restored = new String(bytes);   // デフォルト文字コードでデコード
Java

同じ環境・同じプロセス内だけで完結するなら、
たまたまこれで動いてしまうこともあります。

しかし、

ファイルを別環境で読む
データをネットワーク経由で「他のシステム」に渡す
Java のバージョンや OS の設定が変わる

などのタイミングで、デフォルト文字コードが違ってくると、一気に文字化けリスクが高まります。

Java 18 以降はデフォルトが UTF-8 に統一されましたが、
それ以前は OS 依存(Windows なら MS932/Shift_JIS 系など)が多く、
過去のコードではこの影響で「環境依存文字化け」がよく起きました。

実務的には、getBytes()new String(bytes) の「引数なし版」は使わない方針にしておくと安全です。
常に StandardCharsets.UTF_8 などを明示しましょう。


Charset と Reader/Writer(ファイル I/O とセットで使う)

ファイル読み書き時に Charset を指定する

文字コードが一番ダイレクトに効いてくるのが「ファイル I/O」です。

InputStreamReaderOutputStreamWriter は、
バイトストリームと文字ストリームの橋渡しをするクラスですが、
ここでも Charset を指定できます。

import java.io.*;
import java.nio.charset.StandardCharsets;

try (Reader reader = new InputStreamReader(
        new FileInputStream("input.txt"),
        StandardCharsets.UTF_8)) {

    int ch;
    while ((ch = reader.read()) != -1) {
        System.out.print((char) ch);
    }
}
Java
try (Writer writer = new OutputStreamWriter(
        new FileOutputStream("output.txt"),
        StandardCharsets.UTF_8)) {

    writer.write("こんにちは");
}
Java

ここで UTF-8 を明示しておけば、
「このファイルは UTF-8 で書かれている/読み取られる」とはっきり決まります。

もしここで Charset を指定せずにデフォルトに任せると、

開発マシンではたまたま正しく見える
別環境や別 OS で読むと文字化けする

といった「移植性の低いコード」になりがちですx


よく使う Charset と、その実務での感覚

UTF-8 を基本にする

現在の新規開発では、UTF-8 を標準にするのがほぼ定番です。

理由は、

世界中の文字を扱える
多くの言語・ツールが標準でサポートしている
Java でも StandardCharsets.UTF_8 で明示しやすい

からです。

コード上でも、まずは UTF-8 をベースに考えておくとよいです。

String text = "日本語";
byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
String restored = new String(bytes, StandardCharsets.UTF_8);
Java

既存システムでは Shift_JIS / MS932 なども登場する

日本の古い Windows 系業務システムでは、
Shift_JIS やその派生(MS932 / windows-31j)もまだ残っています。

そういう環境と連携するときは、

相手が何の文字コードを使っているか
自分は何の Charset で読んで/書いているか

を常に意識する必要があります。

Charset.forName("Shift_JIS") で取りつつ、
本当にそれでいいか、仕様書や相手側の設定を確認する癖をつけておくと安全です。


まとめ:初心者が Charset でまず押さえておくべきこと

Charset を、初心者向けにざっくりまとめるとこうなります。

Java の String は内部的に Unicode(UTF-16)。
外の世界(ファイル・ネットワーク・バイト配列)とやりとりするときに、「どの文字コードで変換するか」を決めるのが Charset
getBytes(charset)new String(bytes, charset) で、「文字列 ↔ バイト列」を、指定した Charset で変換する。
デフォルト文字コード(引数なしの getBytes / new String に依存)は危険なので、基本は StandardCharsets.UTF_8 などを明示する。
ファイル I/O の InputStreamReader / OutputStreamWriter にも Charset を指定して、「このファイルは何で書いているか」をコードで固定する。

タイトルとURLをコピーしました