◆ 複雑
◆ Shift_JIS → Shift_JISX0213 → Shift_JIS-2004
◆ Windows のは Windows-31J
  ◆ Shift_JIS に追加された IBM と NEC 拡張をマージしたもの
  ◆ これができてからは CP932 はこれのこと
◆ Shift_JISX0213 と Shift_JIS-2004 は WIndows-31J と互換性なし
  ◆ 片方でしか使えない文字がある

Shift_JIS は種類がいろいろあるとは聞いてましたが 複雑ですし そもそも Shift_JIS なんか使わないし必要ならライブラリ任せでいいのであんまり気にしてませんでした
ですが 出力で Shift_JIS 形式にする必要があって 出力したものが読み取れないとか問題が出たので今更ながら調べることになりました

一応 wikipedia にまとまってはいるのですが 歴史的な話やらその他規格名がいっぱいで頭に入ってきません
https://ja.wikipedia.org/wiki/Shift_JIS
https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932

簡単にまとめると Windows では Shift_JIS ができた頃に CP932 という名前をつけたけど メーカーごとに中身がバラバラだったからまとめたのが Windows-31J らしいです

今ではどこのメーカーでも OS の中身は一緒でデフォルトで入ってるソフトがあるくらいの違いですけど 昔はエンコーディングが違うレベルに違いがあったんですね
ガラケーの絵文字とか機種依存文字みたいのでしょうか
統合時には IBM と NEC が採用していたものをマージして 一部重複もあるようです

名前が Windows-31J なのは Windows 3.1 を出すときに作ったものだからのようです
⇩ は Wiki からコピペですが 昔の Windows には系統があったみたいですね

DOS系 : 1.0・2.x・3.x
9x系 : 95・98・98 SE・Me
NT系 : NT (3.1・3.5x (Citrix WinFrame)・Cairo・4.0)・2000 (Neptune)・XP (x64)・Vista・7・8.x・RT・10

XP も 7 も 10 も NT 系で それより前にあった DOS 系の最後のバージョンが 3.1 だそうです
日本以外ではもう少し続いたそうですけど

この 3.1 が出たのは 1993 年なのでかなり昔ですね
私がまともに PC 使ったのは XP 以降なので全く知らない時代です
そんな頃に Windows-31J が出てたなら Shift_JIS はこれで統一してくれればいいのにされてないわけです

最初に JIS X 0208:1997 で仕様が定義されて
JIS X 0208 の拡張規格の JIS X 0213 で Shift_JISX0213 が定められて
10 文字追加の改正で Shift_JIS-2004 という名前になったようです
上から 1997 年 2000 年 2004 年なので Shift_JIS-2004 が最新みたいです

それと比べて Windows-31J は 1993 年でこっちは特に変化はないみたいなので互換性はないようです
https://ja.wikipedia.org/wiki/Shift_JIS-2004#Windows-31J%E3%81%A8%E3%81%AE%E9%9D%9E%E4%BA%92%E6%8F%9B

Shift_JIS と言うと こういう Windows 特有の Shift_JIS もありますし Windows 以外の正式な仕様のものもあります
Windows で使われる CP932 という言葉も Windows-31J が出てからは Windows-31J のことですが それまでのはまた別の意味です
Java だとその区別のために Windows-31J のことを MS932 と呼んで IBM は Windows-31J を指す CP943 という別のコードポイントを作ったりで用語が多すぎです

プログラミング言語

JavaScript

調べるきっかけになったのは JavaScript の変換だったので iconv-lite を調べてみました
すると whatwg の仕様に沿ってるそうです
https://encoding.spec.whatwg.org/#shift_jis

この仕様はウェブの TextEncoder/TextDecoder に使われるものです
なので TextDecoder でデコードできる Shift_JIS と同じものです (TextEncoder は UTF-8 のみサポート)

Shift_JIS 関係のエンコーディングは Shift_JIS ひとつです
Shift_JIS というエンコーディングには次のラベルがついてます

  • "csshiftjis"
  • "ms932"
  • "ms_kanji"
  • "shift-jis"
  • "shift_jis"
  • "sjis"
  • "windows-31j"
  • "x-sjis"

つまり shift-jis も sjis も ms932 も windows-31j も全部同じものです
実際 Windows くらいでしか使われてない Shift_JIS なのに Windows 以外の仕様に対応させるだけムダってことなんでしょうか
これはこれでありがたいですが もし相手側が Windows-31J 以外の Shift_JIS を作ったり受け取ったりするのなら iconv-lite じゃ対応できなそうです

.NET

.NET も調べてみました
https://docs.microsoft.com/ja-jp/dotnet/api/system.text.encoding?view=netcore-3.1

Windows ですし Shift_JIS 系は Windows-31J だけのサポートでした
コードページの 932 を指定するか名前で shift_jis を指定します

PHP

PHP ではモバイルや Mac の Shift_JIS にも対応してるようです
https://www.php.net/manual/ja/mbstring.supported-encodings.php

  • SJIS
  • SJIS-win
  • CP932
  • SJIS-mac (エイリアス: MacJapanese)
  • SJIS-Mobile#DOCOMO (エイリアス: SJIS-DOCOMO)
  • SJIS-Mobile#KDDI (エイリアス: SJIS-KDDI)
  • SJIS-Mobile#SOFTBANK (エイリアス: SJIS-SOFTBANK)

SJIS-win は Windows-31J のことだと思うのですが SJIS と CP932 がどれを指しているのかわかりませんでした
エイリアスと書いてない以上 3 つともそれぞれ別物みたいですけど

mbstring のソースコードを見てみると SJIS と SJIS_2004 と CP932 になっていて項目が違いますしよくわかりません

Python

Python でもいくつか対応していて これが一番わかりやすい気がしました
https://docs.python.org/ja/3/library/codecs.html#standard-encodings

  • cp932
  • shift_jis
  • shift_jis_2004
  • shift_jisx0213

最初の Shift_JIS と各版の Shift_JIS 2 種類と Windows の Shift_JIS です

比較に良さそうなのでちょっとした関数を作ってみました

sjis = [
"shift_jis",
"shift_jisx0213",
"shift_jis_2004",
"cp932",
]

def enc(c):
print("encode :", c)
mx = max(map(len, sjis))
for e in sjis:
encoded = ""
try:
encoded = ["%02x" % x for x in c.encode(e)]
except:
encoded = "ERROR"
print(" ", e.ljust(mx), ":", encoded)

def dec(b):
print("decode :", b)
b = bytes.fromhex("".join(b))
mx = max(map(len, sjis))
for e in sjis:
decoded = ""
try:
decoded = b.decode(e)
except:
decoded = "ERROR"
print(" ", e.ljust(mx), ":", decoded)

enc("abcあいう")
dec(["61", "62", "63", "82", "a0", "82", "a2", "82", "a4"])
encode : abcあいう
shift_jis : ['61', '62', '63', '82', 'a0', '82', 'a2', '82', 'a4']
shift_jisx0213 : ['61', '62', '63', '82', 'a0', '82', 'a2', '82', 'a4']
shift_jis_2004 : ['61', '62', '63', '82', 'a0', '82', 'a2', '82', 'a4']
cp932 : ['61', '62', '63', '82', 'a0', '82', 'a2', '82', 'a4']
decode : ['61', '62', '63', '82', 'a0', '82', 'a2', '82', 'a4']
shift_jis : abcあいう
shift_jisx0213 : abcあいう
shift_jis_2004 : abcあいう
cp932 : abcあいう

for ch in "∵∑髙№𠮟噓":
enc(ch)
encode : ∵
shift_jis : ['81', 'e6']
shift_jisx0213 : ['81', 'e6']
shift_jis_2004 : ['81', 'e6']
cp932 : ['81', 'e6']
encode : ∑
shift_jis : ERROR
shift_jisx0213 : ERROR
shift_jis_2004 : ERROR
cp932 : ['87', '94']
encode : 髙
shift_jis : ERROR
shift_jisx0213 : ERROR
shift_jis_2004 : ERROR
cp932 : ['ee', 'e0']
encode : №
shift_jis : ERROR
shift_jisx0213 : ['87', '82']
shift_jis_2004 : ['87', '82']
cp932 : ['87', '82']
encode : 𠮟
shift_jis : ERROR
shift_jisx0213 : ERROR
shift_jis_2004 : ['98', '73']
cp932 : ERROR
encode : 噓
shift_jis : ERROR
shift_jisx0213 : ERROR
shift_jis_2004 : ['ea', 'a5']
cp932 : ERROR