Java

UTF-8 / SJIS 文字コード変換できない文字を検知する [Java]

文字コードを変換する際に、UTF-8 には存在するが Shift-JIS には存在しないという文字がある場合、文字化けが発生します。

環境依存文字サロゲートペア文字と言われているものが代表的ですが、文字コードの変換をする際にこれらの文字に出くわすと厄介ですよね。

今回の記事では、UTF-8からShift-JISに変換できるかを判定するサンプルコードを紹介します。(2022/3 更にシンプルな解決策を追記しました)

また、文字コードについての前提知識を得たい方は、以下の記事も合わせてご覧ください。

【超初心者向け】文字コードとは何なのか?

この記事では、超初心者向けに文字コードとは何か?について解説しています。 プログラミングに限らず、パソコンやコンピュータを使っていれば誰でも文字化けが発生した経験があると思います。 今回は、プログラミ ...

続きを見る

基本的な考え方

ある文字列を UTF-8Shift-JIS にそれぞれ変換します。

変換前と変換後の文字列が一致する場合は変換可、文字列が一致しない場合は変換不可」と判定できます。

サンプルコードはJavaの例ですが、どの言語でも同じように処理すればOKだと思います。

文字化けする文字の一覧

代表的な文字化けを引き起こす文字をカテゴリーごとに一覧にします。

これらの文字を使って、文字化けが発生することを確認することが出来ます。

文字カテゴリー
髙(はしご高)IBM拡張漢字
①(丸数字)
Ⅳ(ローマ数宇)
NEC特殊文字
🍣🍺(寿司ビール)絵文字
俱(1面14区)
顗(1面94区)
JIS第3水準漢字
么(2面1区)
鳦(2面94区)
JIS第4水準漢字
サロゲートペア文字(D840 DC0B)サロゲートペア文字
文字化け発生文字一覧

文字化け判定サンプルコード

このサンプルコードでは、isConvert メソッドで文字コード変換可否の判定をします。

private static boolean isConvert(String text) {
     String utf8Text = new String(
               text.getBytes(StandardCharsets.UTF_8),
               StandardCharsets.UTF_8);

     String sjisText = new String(
               text.getBytes(Charset.forName("Shift_JIS")),
               Charset.forName("Shift_JIS"));

     return utf8Text.equals(sjisText);
}

【追記】さらにシンプルな判定方法

CharsetEncoder を使うことで、もっと簡単に判定することができます。

import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;

private static boolean isConvert(String text) {
     CharsetEncoder encoder = Charset.forName("Shift_JIS").newEncoder();
     return encoder.canEncode(text);
}

サンプルコードの検証

実際に文字化けが発生する文字を使って、上記のサンプルコードを検証します。

文字化けする文字として、以下の例を準備しました。

  • はしご高
  • ローマ数字
  • \uD840\uDC0B サロゲートペア文字

※(補足)Javaの内部文字コードはUTF-16 であり、Stringオブジェクトの中身は UTF-16 で保持されています。

public static void main(String args[]) {
    String text = "このテキストは文字化けの起きない文字列です。";
    System.out.println(isConvert(text) + ": " + text);

    text = "髙"; // はしご高 (文字化けする)
    System.out.println(isConvert(text) + ": " + text);

    text = "Ⅳ"; // ローマ数字 (文字化けする)
    System.out.println(isConvert(text) + ": " + text);

    text = "\uD840\uDC0B"; // サロゲートペア文字 (文字化けする)
    System.out.println(isConvert(text) + ": " + text);
}

出力結果

true: このテキストは文字化けの起きない文字列です。
false: 髙
false: Ⅳ
false: ??

まとめ

最後に、この記事の内容をまとめます。

要点まとめ

  • 文字列を一度 UTF-8 と Shift-JIS のバイナリデータに変換する
  • バイナリデータから再度文字列に戻したもの同士が一致するか比較する
  • 一致する場合は文字化けしない、一致しない場合は文字化けすると判定する

文字コードの問題は、一部の特殊な文字でしか顕在化しないので発見が困難なことがあります。

ユーザが入力した氏名などは、見た目上見分けがつかないが実際には文字化けしたりしなかったりするケースがあるため、そのようなユースケースが想定される場合には、何らかの対策を取る必要があります。

今回紹介コードで異常を検知することができるので、適切にログやアラートを出すのも一つの解決策になるかと思います。
※業務で氏名などの個人情報をログに出力するのは大抵の場合NGなので気をつけて下さい

参考

この記事は役に立ちましたか?

  • この記事を書いた人
アバター画像

ゴイチ

ソフトウェアエンジニア歴20年。 C/C++、Java、C#、Kotlinが得意で、組込系からAndroidアプリ、大規模なWebサービスなど幅広いプログラミング経験があります。 現在は某SNSの会社でWebエンジニアをしています。

-Java
-,