

Win10 Pro, Python3.7です。

Shift-jisの文字（外字）を

[code]

    "髙".encode('cp932').hex()
    
[/code]

のような変換で16進数に変換したいのですが、以下サイトと一致しません。  
<https://dencode.com/ja/string/hex>

実行結果  
Python: 'eee0'  
サイト: FBFC

PythonでもFBFCと出るようにしたいです。

ちなみに

[code]

    "髙".encode('Shift-jis').hex()
    
[/code]

だとエラーになるようです。

わかる方、よろしくお願いいたします。


CC BY-SA 4.0
2020-07-30T15:42:35.683
69128
2020-08-01T05:43:59.277
12457
question
python shift-jis
PythonによるShift-jisの文字コードの16進数化


[ウィクショナリーの「髙」のページ](https://ja.wiktionary.org/wiki/%E9%AB%99)
に書いてありますが、まず、正式な意味での Shift_JIS
（区切り文字にアンダースコアを使うのが正式名称です）に「髙」の字はありませんので、後者の例が失敗するのは正常です。「髙」の文字の CP932
としてのバイト列は（CP932 の解釈にもよりますが）、"EEE0" と "FBFC" の2通りがあり、どちらも正しいものです。

「髙」の文字は
[NEC選定IBM拡張文字](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-31J_%E3%81%AB%E9%87%8D%E8%A4%87%E7%99%BB%E9%8C%B2%E3%81%95%E3%82%8C%E3%81%9F%E3%82%B3%E3%83%BC%E3%83%89)
というもので、歴史的な事情で同じ CP932 でも IBM と NEC が同じ文字に違うコードを割り当ててしまった文字の1つです（ですから、"CP932"
という呼び方は1つのエンコーディング方式を指しておらず、厳密には曖昧です）。Microsoft はその後両者を統合して Windows-31J
という統一した文字コードを採用することにしました。この統合は互換性を維持する形で行われたので、結果として同じ文字にコードが2つずつ割り当てられることになってしまいました。

それぞれの言語のライブラリで "CP932" をどのように扱うかは微妙な差があるようですが、 Python では "Windows-31J"
というエンコーディング名は登録されておらず、 "CP932" が実質的に Windows-31J を指しているようです。ですから、デコードすると
"EEE0" も "FBFC" も "髙" の文字になります：

[code]

    >>> b'\xEE\xE0'.decode('cp932')
    '髙'
    >>> b'\xFB\xFC'.decode('cp932')
    '髙'
    
[/code]


CC BY-SA 4.0
2020-07-30T18:06:46.880
69131
2020-07-30T18:06:46.880
40595
69128
answer


@Yuta Kitamuraさん回答にある紹介記事と、あと2つくらいの記事で補足すると以下になります。

[髙 - ウィクショナリー日本語版 - Wiktionary](https://ja.wiktionary.org/wiki/%E9%AB%99)

記事の下の方に-1990までのJIS X 0208系のShift
JISにIBM拡張漢字として0xFBFC、NEC選定IBM拡張漢字として0xEEE0が入っていると記述されています。

[Fehlersuchen und Verzeichnisse meines Windows](http://www.dennougedougakkai-
ndd.org/alte/2te/tagebuecher/windows.html)

このページで「02.05.27 はしご高」とタイトルされたパートの後半で、Windows上ではIME入力やコード変換で0xFBFCが選択されるとあります。

コードが重複している場合の動作は@Yuta
Kitamuraさん2つ目の紹介記事の中に「3.「NEC選定IBM拡張文字」「IBM拡張文字」が重複する場合は、「IBM拡張文字」に統一」とあり「はしごだか」が例示されているため、0xFBFCが実情のようです。

いずれもPythonでの動作(0xEEE0になる)とは違っていますね。

[ｼﾌﾄJIS漢字ｺｰﾄﾞについての質問です。
漢字ｺｰﾄﾞ...](https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q1099379515)

このQ&A記事の回答に歴史的経緯とかがダイジェストで解説されています。  
短くざっくりした内容としてならこちらでも良いかも。

* * *

試しに以下のようにPythonで「Microsoftコードページ932 - Wikipedia」の「Windows-31J
に重複登録されたコード」の表を変換してみたら、 **「3.「NEC選定IBM拡張文字」「IBM拡張文字」が重複する場合は、「IBM拡張文字」に統一」**
という仕組みはPythonでは適用されていないようですね。全て「IBM拡張文字」ではなく「NEC選定IBM拡張文字」のコードで変換されました。何か使い方が違うのかもしれませんが。

それからそのページの「凞」「蘒」はコードが違うらしく変換出来ませんでしたので他の記事[IBM拡張文字](https://www.wdic.org/w/WDIC/IBM%E6%8B%A1%E5%BC%B5%E6%96%87%E5%AD%97)から拾って変更しました。try:
except: はその名残りです。

Windows10 64bit 1909, Python 3.8.5 で試しています。

[code]

    extchars = list('￢∵≒≡∫√⊥∠∩∪①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡㍻〝〟㏍㊤㊥㊦㊧㊨㈲㈹㍾㍽㍼∮∑∟⊿ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ№℡㈱ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹ￤＇＂纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤硺礰礼神祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑')
    
    with open('extcharscp932.txt', 'w', encoding='utf-8', newline='') as f:
        for c in extchars:
            try:
                s = c + ' = ' + c.encode('cp932').hex() + '\n'
            except:
                s = c + ' = utf-8 ' + c.encode('utf-8').hex() + " is can't encode to cp932\n"
            
            f.write(s)
    
    
[/code]

* * *

ではPythonではなくMicrosoftの言語やライブラリではどうなっているか、.NET Core 3.1
C#のコンソールアプリで試すと、Wikipediaの記述のように **...「IBM拡張文字」に統一** されていました。

[code]

    using System;
    using System.IO;
    namespace ConsoleApp1
    {
        class Program
        {
            static void Main(string[] args)
            {
                System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
                System.Text.Encoding sjis = System.Text.Encoding.GetEncoding("shift_jis");
                string extchars = "￢∵≒≡∫√⊥∠∩∪①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳㍉㌔㌢㍍㌘㌧㌃㌶㍑㍗㌍㌦㌣㌫㍊㌻㎜㎝㎞㎎㎏㏄㎡㍻〝〟㏍㊤㊥㊦㊧㊨㈲㈹㍾㍽㍼∮∑∟⊿ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩ№℡㈱ⅰⅱⅲⅳⅴⅵⅶⅷⅸⅹ￤＇＂纊褜鍈銈蓜俉炻昱棈鋹曻彅丨仡仼伀伃伹佖侒侊侚侔俍偀倢俿倞偆偰偂傔僴僘兊兤冝冾凬刕劜劦勀勛匀匇匤卲厓厲叝﨎咜咊咩哿喆坙坥垬埈埇﨏塚增墲夋奓奛奝奣妤妺孖寀甯寘寬尞岦岺峵崧嵓﨑嵂嵭嶸嶹巐弡弴彧德忞恝悅悊惞惕愠惲愑愷愰憘戓抦揵摠撝擎敎昀昕昻昉昮昞昤晥晗晙晴晳暙暠暲暿曺朎朗杦枻桒柀栁桄棏﨓楨﨔榘槢樰橫橆橳橾櫢櫤毖氿汜沆汯泚洄涇浯涖涬淏淸淲淼渹湜渧渼溿澈澵濵瀅瀇瀨炅炫焏焄煜煆煇凞燁燾犱犾猤猪獷玽珉珖珣珒琇珵琦琪琩琮瑢璉璟甁畯皂皜皞皛皦益睆劯砡硎硤硺礰礼神祥禔福禛竑竧靖竫箞精絈絜綷綠緖繒罇羡羽茁荢荿菇菶葈蒴蕓蕙蕫﨟薰蘒﨡蠇裵訒訷詹誧誾諟諸諶譓譿賰賴贒赶﨣軏﨤逸遧郞都鄕鄧釚釗釞釭釮釤釥鈆鈐鈊鈺鉀鈼鉎鉙鉑鈹鉧銧鉷鉸鋧鋗鋙鋐﨧鋕鋠鋓錥錡鋻﨨錞鋿錝錂鍰鍗鎤鏆鏞鏸鐱鑅鑈閒隆﨩隝隯霳霻靃靍靏靑靕顗顥飯飼餧館馞驎髙髜魵魲鮏鮱鮻鰀鵰鵫鶴鸙黑";
                using (StreamWriter sw = File.CreateText("extcharssjis.txt"))
                {
                    foreach (char c in extchars)
                    {
                        sw.WriteLine(c + " = " + BitConverter.ToString(sjis.GetBytes(new char[] { c })).Replace("-", "").ToLower());
                    }
                }
            }
        }
    }
    
[/code]

* * *

標準機能の単純な呼び出しでは該当16進数が得られないのだから、こんな対策が考えられます。

  * NEC選定IBM拡張文字の範囲(edxx - eexx)があったら、IBM拡張文字(faxx - fcxx)に変換する  
単純な数値の足し引き等では駄目そうなので1対1の変換テーブルを作るとかでしょうか

  * Microsoftと互換のencodeを行うライブラリを探して利用する  
ちょっと検索した程度では見つかりませんが、どこかにはあるかもしれません

  * Microsoftと互換のencodeを行うライブラリを自分で作る  
この辺の記事が同様の問題を扱っているでしょう  
[How do I properly create custom text
codecs?](https://stackoverflow.com/q/38777818/9014308)  
[Custom Python Charmap Codec](https://stackoverflow.com/q/41230733/9014308)


CC BY-SA 4.0
2020-07-30T18:16:41.890
69132
2020-08-01T05:43:59.277
2020-08-01T05:43:59.277
26370
26370
69128
answer