12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【いつもわからなくなるシリーズ】UTF-8とSJISの変換で文字化けする仕組み

Last updated at Posted at 2022-03-06

はじめに

  • 「~」などの文字が SJIS と UTF-8 で変換すると文字化けをします。
  • 調べると【なんとなく】わかるのですが、しばらくすると、いつもわからなくなりこんがらがるので、まとめておきます。

文字化けする

文字化けする理由はいくつかあります。その中でも、SJIS と UTF-8 の変換によってで文字化けすることがあります。
これは、SJIS の 文字コード には「Shift_JIS」や「MS932」「Windows-31J」などあります。
この変換によって、文字化けします。

これ → 「~」 "から" ですね。
この文字が化けることがあります。(ほかにもありますが判りにくいので、一旦この文字で検証!)

まず、この文字のコードを「Shift_JIS」「Windows-31J (MS932)」で確認しましょう。

文字 コード
0x8160

※「Windows-31J」と「MS932」は名前が違うが同じもの

この文字を「Shift_JIS」「Windows-31J」のそれぞれで「UTF-8」に変換しましょう。

Java でコードを書いて検証しましょう。

public static void readWrite(String inputFile, String inputCharsetName, String outputFile, String outputCharsetName) {
    FileInputStream    fileInputStream    = null;
    InputStreamReader  inputStreamReader  = null;

    FileOutputStream   fileOutputStream   = null;
    OutputStreamWriter outputStreamWriter = null;

    System.out.println("-----------");
    System.out.println("inputFile:" + inputFile);
    System.out.println("inputCharsetName:" + inputCharsetName);
    System.out.println("outputFile:" + outputFile);
    System.out.println("outputCharsetName:" + outputCharsetName);
    System.out.println("");

    try {
        fileInputStream    = new FileInputStream(inputFile);
        fileOutputStream   = new FileOutputStream(outputFile);

        inputStreamReader  = new InputStreamReader(fileInputStream, inputCharsetName);
        outputStreamWriter = new OutputStreamWriter(fileOutputStream, outputCharsetName);

        int ch;
        while ((ch = inputStreamReader.read()) != -1) {
            System.out.print(Integer.toHexString(ch) + " ");

            outputStreamWriter.write(ch);
        }

    } catch (IOException e) {
        System.out.println(e);

    } finally {
        if (outputStreamWriter != null) {
            try {
                outputStreamWriter.close();
            } catch (IOException e) {}
        }
        if (inputStreamReader != null) {
            try {
                inputStreamReader.close();
            } catch (IOException e) {}
        }
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {}
        }
        if (fileInputStream != null) {
            try {
                fileInputStream.close();
            } catch (IOException e) {}
        }
    }
    System.out.println("");

}
        readWrite("./in/SJIS.txt", "Windows-31J", "./out/Windows-31J_to_UTF8.txt", "UTF-8");
        readWrite("./in/SJIS.txt", "Shift_JIS", "./out/Shift_JIS_to_UTF8.txt", "UTF-8");
/in/SJIS.txt (入力のファイル)
/out/Windows-31J_to_UTF8.txt(出力した結果のファイル)
/out/Shift_JIS_to_UTF8.txt (出力した結果のファイル)
実行結果
-----------
inputFile:./in/SJIS.txt
inputCharsetName:Windows-31J
outputFile:./out/Windows-31J_to_UTF8.txt
outputCharsetName:UTF-8

ff5e 
-----------
inputFile:./in/SJIS.txt
inputCharsetName:Shift_JIS
outputFile:./out/Shift_JIS_to_UTF8.txt
outputCharsetName:UTF-8

301c 

なんと、元が「~」から「文字コード」によって UTF-8 上違う文字・コードになりました。

文字コード コード
Shift_JIS U+301C
Windows-31J U+FF5E

UTF-8 から改めて、元の文字コードを利用して戻すと、「~」に戻りますが、

  • Windows-31J で読み込み、UTF-8 に変換、Shift_JIS で出力
  • Shift_JIS で読み込み、UTF-8 に変換、Windows-31J で出力
    のように異なる文字コードにすると、文字化けします。
SJIS上の文字 読むときの文字コード UTFの文字 書き込むときの文字コード できたSJIS上の文字
~ (0x8160) Windows-31J ~ (U+FF5E) Windows-31J ~ (0x8160)
~ (0x8160) Shift_JIS 〜 (U+301C) Shift-JIS ~ (0x8160)
~ (0x8160) Windows-31J ~ (U+FF5E) Shift-JIS 文字化け
~ (0x8160) Shift_JIS 〜 (U+301C) Windows-31J 文字化け

さて、読むときと書くときで、違う文字コードになることはあるのでしょうか。

それは、「クライアントが Windows、サーバが Unix系 でデータをやり取りする場合」です。

以下のような対処が必要になってきます。
クライアント Windows から サーバ Unix系 へデータを送信する場合
 ・Windows-31J で読み込み、UTF-8 に変換。
 ・「~ (U+FF5E)」を「〜 (U+301C)」に変換。
 ・サーバ側 Unix系にデータを送信。

サーバ Unix系 から クライアント Windows へデータを受信する場合
 ・サーバ側 Unix系からデータを受信。
 ・「〜 (U+301C)」を「~ (U+FF5E)」に変換。
 ・Windows-31J で書き込む。

SJIS上の文字 読むときの文字コード UTFの文字 書き込むときの文字コード
~ (0x8160) Windows-31J ~ (U+FF5E) Windows-31J
~ (0x8160) Shift_JIS 〜 (U+301C) Shift-JIS
~ (0x8160) Windows-31J ~ (U+FF5E)から「〜 (U+301C)」に変換 Shift-JIS
~ (0x8160) Shift_JIS 〜 (U+301C)から「~ (U+FF5E)」に変換 Windows-31J

このような対処が必要になる文字として、以下の文字があります。
UTF-8 上で、文字の変換が必要になります。

SJIS Windows-31J で読み込んでUTF-8へ変換 Shift-JIS で読み込んでUTF-8へ変換
~ (0x8160) ~ (U+FF5E) 〜 (U+301C)
∥ (0x8161) ∥ (U+2225) ‖ (U+2016)
- (0x817C) - (U+FF0D) − (U+2212)
¢ (0x8191) ¢ (U+FFE0) ¢ (U+00A2)
£ (0x8192) £ (U+FFE1) £ (U+00A3)
¬ (0x81CA) ¬ (U+FFE2) ¬ (U+00AC)
― (0x81CA) ― (U+2015) — (U+2014)

では、その変換を入れたコードにしてみましょう。

readWriteForWindows31JtoShiftJIS("./in/SJIS.txt", "Windows-31J", "./out/Windows-31J_to_Shift_JIS.txt", "Shift_JIS");
readWriteForShiftJIStoWindows31J("./in/SJIS.txt", "Shift_JIS", "./out/Shift_JIS_to_Windows-31J.txt", "Windows-31J");
SJIS.txt
~∥-¢£¬―
readWriteForWindows31JtoShiftJISメソッド
public static void readWriteForWindows31JtoShiftJIS(String inputFile, String inputCharsetName, String outputFile, String outputCharsetName) {
    FileInputStream    fileInputStream    = null;
    InputStreamReader  inputStreamReader  = null;

    FileOutputStream   fileOutputStream   = null;
    OutputStreamWriter outputStreamWriter = null;

    System.out.println("-----------");
    System.out.println("inputFile:" + inputFile);
    System.out.println("inputCharsetName:" + inputCharsetName);
    System.out.println("outputFile:" + outputFile);
    System.out.println("outputCharsetName:" + outputCharsetName);
    System.out.println("");

    try {
        fileInputStream    = new FileInputStream(inputFile);
        fileOutputStream   = new FileOutputStream(outputFile);

        inputStreamReader  = new InputStreamReader(fileInputStream, inputCharsetName);
        outputStreamWriter = new OutputStreamWriter(fileOutputStream, outputCharsetName);

        int ch;
        while ((ch = inputStreamReader.read()) != -1) {
            System.out.print(Integer.toHexString(ch) + " ");

            switch (ch) {
            case 0xff5e:
                ch = 0x301c;
                break;
            case 0x2225:
                ch = 0x2016;
                break;
            case 0xff0D:
                ch = 0x2212;
                break;
            case 0xFFE0:
                ch = 0x00A2;
                break;
            case 0xFFE1:
                ch = 0x00A3;
                break;
            case 0xFFE2:
                ch = 0x00AC;
                break;
            case 0x2015:
                ch = 0x2014;
                break;
            }

            outputStreamWriter.write(ch);

        }

    } catch (IOException e) {
        System.out.println(e);

    } finally {
        if (outputStreamWriter != null) {
            try {
                outputStreamWriter.close();
            } catch (IOException e) {}
        }
        if (inputStreamReader != null) {
            try {
                inputStreamReader.close();
            } catch (IOException e) {}
        }
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {}
        }
        if (fileInputStream != null) {
            try {
                fileInputStream.close();
            } catch (IOException e) {}
        }
    }
    System.out.println("");

}
readWriteForShiftJIStoWindows31Jメソッド
public static void readWriteForShiftJIStoWindows31J(String inputFile, String inputCharsetName, String outputFile, String outputCharsetName) {
    FileInputStream    fileInputStream    = null;
    InputStreamReader  inputStreamReader  = null;

    FileOutputStream   fileOutputStream   = null;
    OutputStreamWriter outputStreamWriter = null;

    System.out.println("-----------");
    System.out.println("inputFile:" + inputFile);
    System.out.println("inputCharsetName:" + inputCharsetName);
    System.out.println("outputFile:" + outputFile);
    System.out.println("outputCharsetName:" + outputCharsetName);
    System.out.println("");

    try {
        fileInputStream    = new FileInputStream(inputFile);
        fileOutputStream   = new FileOutputStream(outputFile);

        inputStreamReader  = new InputStreamReader(fileInputStream, inputCharsetName);
        outputStreamWriter = new OutputStreamWriter(fileOutputStream, outputCharsetName);

        int ch;
        while ((ch = inputStreamReader.read()) != -1) {
            System.out.print(Integer.toHexString(ch) + " ");

            switch (ch) {
            case 0x301c:
                ch = 0xff5e;
                break;
            case 0x2016:
                ch = 0x2225;
                break;
            case 0x2212:
                ch = 0xff0D;
                break;
            case 0x00A2:
                ch = 0xFFE0;
                break;
            case 0x00A3:
                ch = 0xFFE1;
                break;
            case 0x00AC:
                ch = 0xFFE2;
                break;
            case 0x2014:
                ch = 0x2015;
            }

            outputStreamWriter.write(ch);

        }

    } catch (IOException e) {
        System.out.println(e);

    } finally {
        if (outputStreamWriter != null) {
            try {
                outputStreamWriter.close();
            } catch (IOException e) {}
        }
        if (inputStreamReader != null) {
            try {
                inputStreamReader.close();
            } catch (IOException e) {}
        }
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
            } catch (IOException e) {}
        }
        if (fileInputStream != null) {
            try {
                fileInputStream.close();
            } catch (IOException e) {}
        }
    }
    System.out.println("");

}

ご了承ください

  • 自分が何とか学習した内容を記載しており、間違ってはいないと思いますが、そのあたりは生暖かくご覧いただければ、嬉しいです。

参考にさせていただいたページ

参考にさせていただきました、ありがとうございます。

12
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
12
11

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?