文字コードの全体像
文字コードは「文字をバイト列に変換・復元するための規則」です。Java の内部表現は Unicode を採用し、文字列は UTF-16(コードユニット)で保持されます。一方、ファイルやネットワークでは「UTF-8」や「Shift_JIS」のような外部エンコーディングを明示して読み書きします。重要なのは「内部は Unicode」「外部はエンコーディングを必ず指定」の二本柱です。
Java の内部表現(Unicode と UTF-16)
String と char の基礎
Java の String は Unicode を UTF-16 のコードユニット配列として保持します。char は16ビットで1ユニットを表しますが、すべての文字が char 1個で表現できるわけではありません。絵文字や一部の漢字拡張など、コードポイントが大きい文字は「サロゲートペア(char 2個)」になります。
String s = "A😊"; // 'A' は1ユニット、'😊' はサロゲートペア
System.out.println(s.length()); // 3(UTF-16の長さ)
System.out.println(s.codePoints().count()); // 2(Unicodeコードポイント数)
Javaコードポイントで安全に扱う
文字単位の正確な処理(長さ、切り出し、走査)は「コードポイント」APIを使います。length() はコードユニット数、codePoints() は実際の文字数に近いコードポイント列です。
int cpCount = s.codePointCount(0, s.length()); // コードポイント数
int first = s.codePointAt(0); // 先頭コードポイント
String head = new String(Character.toChars(first)); // 正しい1文字切り出し
Java外部エンコーディングの明示(UTF-8 を基本に)
バイト⇔文字列の往復
外部とやり取りする時は、エンコーディングを必ず指定します。未指定だと「環境依存の既定値」になり、文字化けが起きやすくなります。
import java.nio.charset.StandardCharsets;
// 文字列 → バイト
byte[] bytes = "江東区".getBytes(StandardCharsets.UTF_8);
// バイト → 文字列
String text = new String(bytes, StandardCharsets.UTF_8);
Javaファイル読み書きでの指定
ファイル I/O は Files の「文字セットあり」APIか、Reader/Writer で Charset を渡します。UTF-8 を基本とし、必要なら相手仕様に合わせて切り替えます。
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
// 読み取り
String s = Files.readString(Path.of("data.txt"), StandardCharsets.UTF_8);
// 書き込み
Files.writeString(Path.of("out.txt"), "江東区", StandardCharsets.UTF_8);
Javaimport java.io.*;
import java.nio.charset.StandardCharsets;
try (var br = new BufferedReader(new InputStreamReader(
new FileInputStream("in.txt"), StandardCharsets.UTF_8))) {
System.out.println(br.readLine());
}
try (var bw = new BufferedWriter(new OutputStreamWriter(
new FileOutputStream("out.txt"), StandardCharsets.UTF_8))) {
bw.write("江東区");
}
Javaよくある文字化けの原因と対策(重要ポイントの深掘り)
既定エンコーディングの誤用
エンコーディング未指定の I/O は、OSやJDK設定に依存します。プロジェクト全体のルールとして「常に UTF-8 を明示」すると、環境差での文字化けを防げます。
入力と出力の不一致
読み取りと書き込みで異なるエンコーディングを使うと文字化けします。仕様書やAPIドキュメントで「何エンコーディングか」を確認し、コードで揃えます。Web/JSON/CSV などは UTF-8 が事実上の標準です。
BOM(Byte Order Mark)の扱い
UTF-8 の BOM付きファイルは、一部ツールで先頭に不可視文字が混入します。Java の Files.readString は BOM を透過しがちですが、問題が起きるときは BOM を除去して扱うか、BOMなしのUTF-8で出力します。
// 先頭の不可視文字を取り除く例
String normalized = text.replaceFirst("^\uFEFF", "");
JavaShift_JIS / Windows-31J / EUC-JP の罠
日本語レガシー系は機種依存文字や拡張差があり、互換性問題が起こりやすいです。やむを得ず扱う時は Charset.forName("Windows-31J") など、相手仕様に合わせて厳密に指定します。
var sjis = java.nio.charset.Charset.forName("Windows-31J");
byte[] bs = "㈱".getBytes(sjis); // 対応有無に注意
String t = new String(bs, sjis);
Java正規化・比較・並びの注意(Unicode を使いこなす)
同じ見た目でも別コード
合成文字や全角半角など、見た目が同じでもコードポイントが異なる場合があります。厳密な一致が必要なら正規化してから比較します。
import java.text.Normalizer;
String n1 = Normalizer.normalize("パ", Normalizer.Form.NFC);
String n2 = Normalizer.normalize("パ", Normalizer.Form.NFC);
System.out.println(n1.equals(n2)); // true(NFCで揃える)
Java大文字小文字・濁点の比較と整列
言語ルールで並べたい場合は Collator を使います。コードポイント順(compareTo)は人間の感覚とズレることがあります。
import java.text.Collator;
import java.util.Locale;
var col = Collator.getInstance(Locale.JAPAN);
col.setStrength(Collator.PRIMARY); // 大小・濁点を弱める
System.out.println(col.compare("ばなな", "バナナ"));
Java例題で身につける
例 1: ファイルを UTF-8 で安全に往復
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
var p = Path.of("memo.txt");
Files.writeString(p, "江東区 東京", StandardCharsets.UTF_8);
String s = Files.readString(p, StandardCharsets.UTF_8);
System.out.println(s);
Java例 2: ネットワークのバイト列を文字列へ
import java.nio.charset.StandardCharsets;
byte[] payload = /* HTTPレスポンスボディ */;
String json = new String(payload, StandardCharsets.UTF_8); // Content-Typeに従うのが原則
Java例 3: コードポイントで安全に1文字ずつ処理
String text = "A😊東";
text.codePoints().forEach(cp -> {
String ch = new String(Character.toChars(cp));
System.out.println(ch);
});
Java例 4: レガシーとの相互変換
var sjis = java.nio.charset.Charset.forName("Windows-31J");
String s = "東京都 江東区";
byte[] b = s.getBytes(sjis); // SJISへ
String back = new String(b, sjis); // SJISから
System.out.println(back);
Java仕上げのアドバイス(重要部分のまとめ)
文字列の内部は Unicode(UTF-16)、外部 I/O はエンコーディングを必ず指定——まずこの原則を体に入れる。UTF-8 をプロジェクト標準にし、レガシー系は仕様に合わせて明示する。長さや切り出しはコードポイントで安全に行い、比較や並びで人間の感覚を求めるなら正規化や Collator を使う。BOM・既定エンコーディング・読み書き不一致が文字化けの主因——ここを潰せばトラブルは激減します。
