[Java] シフトJISの扱い


# 目的

Java における SJIS と MS932 の相違点と、扱う際の注意点をまとめます。

- SJIS/MS932 での値を 0xHHHH 形式で表記していますが、１バイト目が上位、２バイト目が下位、つまり big endian (network byte order)です。

# いわゆるシフトJIS

いわゆる「シフトJIS」には何種類もありますが、主なものは以下の２つだと思います。

- SJIS (Shift_JIS)  
  Microsoft が作成したシフトJIS符号化法を、JIS X 0208 に取り込んだもの。
  扱える文字集合は当時のJIS漢字(JIS X 0201/0208)の範囲のみ
- MS932 (Windows-31J)  
  Microsoftは、日本語用の文字セットをコードページ932として管理しており、MS-DOS時代には各OEMベンダーでコードページの独自拡張を許可していた。  
  しかしWindowsを作る際にはコードページの独自拡張を許可せず、それまでに広く使われていたIBMとNECの文字セットを統合して、新たなコードページ932を作成した。  
  Javaでは、IBMのコードページ932を"CP932"、Microsoftのコードページ932を"MS932"という名前で扱っている。

※Java以外ではMicrosoftのコードページ932のことも"CP932"と呼ぶことが普通だと思うので注意。

参考：

- [Wikipedia:Shift_JIS](https://ja.wikipedia.org/wiki/Shift_JIS)
- [Wikipedia:Microsoftコードページ932](https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932)

# 文字集合

SJIS/MS932 では扱える文字集合が異なります。

| 文字の種類 | SJIS | MS932 | 補足 |
|:--|:--:|:--:|:--:|
| JIS第1水準 | ○ | ○ |  |
| JIS第2水準 | ○ | ○ |  |
| NEC特殊文字 | × | ○ | 0x8740～0x879c |
| NEC選定IBM拡張文字 | × | ○ | 0xed40～0xeefc |
| ユーザ外字 | × | ○ | 0xf040～0xf9fc |
| IBM拡張文字 | × | ○ | 0xfa40～0xfc4b |

ちなみに、２バイト文字の範囲は以下のとおりです。

- １バイト目： 0x81～0x9f， 0xe0～0xef （MS932 は～0xfc）
- ２バイト目： 0x40～0x7e， 0x80～0xfc

## NEC特殊文字

![NEC.png](https://qiita-image-store.s3.amazonaws.com/0/97350/82c64220-5e71-71dc-a3ac-6a508df9459e.png)

## NEC選定IBM拡張文字

![NEC-IBM.png](https://qiita-image-store.s3.amazonaws.com/0/97350/1a71f1b6-1a00-4335-a3b9-70f9b4abdbf9.png)

※Microsoftはこれらの文字を使わないようにと指示しているらしい。同じ文字がIBM拡張文字にあるからだと思われる。

## IBM拡張文字

![IBM.png](https://qiita-image-store.s3.amazonaws.com/0/97350/08231efc-1d91-b8ed-2119-451de40b793a.png)

# Javaの文字セット名(Charset)

いまさら気にしなくてもよさそうなぐらい古い話ですが、Java におけるCharset名の "Shift_JIS" は途中で意味が変わった（間違えていた）ことがあるらしい。

| Javaバージョン | SJISのCharset名 | MS932のCharset名 |
|:---|:---|:---|
| ～ 1.1.7 | SJIS, **Shift_JIS** | なし |
| 1.1.8 ～ 1.4.0 | SJIS | MS932, Windows-31J, **Shift_JIS** |
| 1.4.1～ | SJIS, **Shift_JIS** | MS932, Windows-31J |

古い環境でも動作させる必要がある場合には、 "Shift_JIS" は使わないほうが良いかもしれない。

※IANAの正式な登録名は "Shift_JIS" なんですけどね。

# SJIS/MS932で異なる文字マッピング

SJISとMS932では、Unicodeコードポイントのマッピングが一部異なります。
具体的には以下の７文字が異なります。

| SJIS/MS932 | SJISでデコード | MS932でデコード |
|:--|:--|:--|
| 0x815c | U+2014 : EM DASH "—" | U+2015 : HORIZONTAL BAR "―" |
| 0x8160 | U+301C : WAVE DASH "〜" | U+FF5E : FULLWIDTH TILDE "～" |
| 0x8161 | U+2016 : DOUBLE VERTICAL LINE "‖" | U+2225 : PARALLEL TO "∥" |
| 0x817c | U+2212 : MINUS SIGN "−" | U+FF0D : FULLWIDTH HYPHEN-MINUS "－" |
| 0x8191 |  U+00A2 : CENT SIGN "¢" |U+FFE0 : FULLWIDTH CENT SIGN "￠" |
| 0x8192 |  U+00A3 : POUND SIGN "£" |U+FFE1 : FULLWIDTH POUND SIGN "￡" |
| 0x81ca |  U+00AC : NOT SIGN "¬" |U+FFE2 : FULLWIDTH NOT SIGN "￢" |

そのため、SJISとMS932の文字コード変換を混在させると、文字化けすることがあります。
また、Unicodeでは別のコードポイントにマッピングされるので、検索不一致などがおきる可能性があります。

| SJIS/MS932 | →SJISでデコード | →MS932でエンコード |
|:--|:--|:--|
| 0x815c | U+2014 : EM DASH "—" | **変換不能** |
| 0x8160 | U+301C : WAVE DASH "〜" | **変換不能** |
| 0x8161 | U+2016 : DOUBLE VERTICAL LINE "‖" | **変換不能** |
| 0x817c | U+2212 : MINUS SIGN "−" | **変換不能** |
| 0x8191 | U+00A2 : CENT SIGN "¢" | 0x8191 |
| 0x8192 | U+00A3 : POUND SIGN "£" | 0x8192 |
| 0x81ca | U+00AC : NOT SIGN "¬" | 0x81ca |

| SJIS/MS932 | →MS932でデコード | →SJISでエンコード |
|:--|:--|:--|
| 0x815c | U+2015 : HORIZONTAL BAR "―" | **変換不能** |
| 0x8160 | U+FF5E : FULLWIDTH TILDE "～" | **変換不能** |
| 0x8161 | U+2225 : PARALLEL TO "∥" | **変換不能** |
| 0x817c | U+FF0D : FULLWIDTH HYPHEN-MINUS "－" | **変換不能** |
| 0x8191 | U+FFE0 : FULLWIDTH CENT SIGN "￠" | **変換不能** |
| 0x8192 | U+FFE1 : FULLWIDTH POUND SIGN "￡" | **変換不能** |
| 0x81ca | U+FFE2 : FULLWIDTH NOT SIGN "￢" | **変換不能** |

※外部データの読み書きだけではなく、ソースやリソースに記載の文字にも同様の問題がありえることに注意。

# MS932の重複文字

MS932 では、同じ文字が複数のコードで表現されていることがあります。

- 「IBM拡張文字」と「NEC選定IBM拡張文字」に同じ字形が存在する。
- 「NEC特殊文字」「NEC選定IBM拡張文字」「IBM拡張文字」と「JIS第２水準」に同じ字形が存在する。

Unicodeのコードポイントは１つしかないため、これらの文字はデコードの際に１つのコードに集約されます。その結果、デコード後にエンコードをすると異なる値になる文字があります。
集約時の優先順位（エンコード時のマッピング）は以下の通りです。

1. JIS第２水準
2. NEC特殊文字
3. IBM拡張文字

いくつか具体例を示します。

| 文字種類 | "√" | "∵" | "￢" | "№" | "㈱" | "Ⅲ" | "﨑" | "髙" | "ⅲ" |
|:--|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|:--:|
| JIS第２水準 | **0x81e3** | **0x81e6** | **0x81ca** | - | - | - | - | - | - |
| NEC特殊文字 | 0x8795 | 0x879a | - | **0x8782** | **0x878a** | **0x8756** | - | - | - |
| NEC選定IBM拡張文字 |  - | - |0xeef9 | - | - | - | 0xed95 | 0xeee0 | 0xeef1 |
| IBM拡張文字 | - | 0xfa5b | 0xfa54 | 0xfa59 | 0xfa58 | 0xfa4c | **0xfab1** | **0xfbfc** | **0xfa42** |
| Unicode | U+221A | U+2235 | U+FFE2 | U+2116 | U+3231 | U+2162 | U+FA11 | U+9AD9 | U+2172 |
| エンコード後 |  0x081e3 | 0x81e6 | 0x81ca | 0x8782 | 0x878a | 0x8756 | 0xfab1 | 0xfbfc | 0xfa42 |