91541


# 現象

subprocessモジュールで、Windowsのsysteminfoを取得し、'shift-jis'デコードすると、環境によって例外が送出される。  
PC1では例外発生：過去にはPC2同様問題なかった。  
PC2では問題ない

ちなみにソースコード上の以下の部分を変更することで、PC1,PC2とも例外は発生しなくなります。  
→ **これで一旦、例外がなくなったのですが、再確認したところ、例外が発生するようになりました。**

[code]

    textout = retout.decode('shift-jis') -> textout = retout.decode()
    
[/code]

# 知りたいこと

  * systeminfoの内容（環境ごとの情報の中に文字コードが異なるものがある可能性）によって発生するのか
  * 環境ごとに発生するとしたら、どのように原因を調べたらよいか？（環境変数にそれらしき違いはありませんでした。）

# 環境

・Windows 10 Pro  
・VScode 1.72.0  
・Python 3.9.10

PC1、PC2ともに上記の環境

# 発生する例外

[code]

    UnicodeDecodeError: 'shift_jis' codec can't decode byte 0x97 in position 2475: illegal multibyte sequence  
    
[/code]

# ソースコード

[code]

    import json
    import subprocess
    
    cmd_list = ['systeminfo', '/fo', 'LIST']
    
    cmdreturn = subprocess.run(
        cmd_list, check=True, stdout=subprocess.PIPE)
    
    retcode = cmdreturn.returncode
    retout = cmdreturn.stdout
    #textout = retout.decode()
    try:
        textout = retout.decode('shift-jis')
    except Exception as e:
        print(e)
        raise e
    
    text_list = textout.splitlines()
    
    
    param_dict = dict()
    c_key = None
    
    for text_line in text_list:
        if not text_line.startswith(' '):
            if ':' in text_line:
                key_word = text_line.split(':', maxsplit=1)
                if key_word[1].startswith(' '):
                    key_word[1] = key_word[1].lstrip()
                param_dict.setdefault(key_word[0], key_word[1])
                c_key = key_word[0]
        else:
            temp = text_line.lstrip()
            if '\t' in temp:
                temp = temp.split('\t')
                temp = ''.join(temp)
            if c_key in param_dict:
                c_data = param_dict[c_key]
                if isinstance(c_data, list):
                    set_list = c_data
                    set_list.append(temp)
                    pass
                else:
                    set_list = [c_data, temp]
                    param_dict[c_key] = set_list
    else:
        ret_dump = json.dumps(param_dict, indent=2, ensure_ascii=False)
    
    print(ret_dump)
    
[/code]

# 追加試験

## 文字コード指定

localeモジュールにより、システムのエンコードを取得し、これをdecode時に指定しました。結果は同じで､0x97がErrorとなります。

バイナリエディタの該当部分をコピペして、サクラエディタに貼り付けると、「・」という表示になりました。  
コマンドプロンプトで、systeminfo /fo LISTを実行した結果の該当行は、以下の表示になります。

[code]

    接続名:           イーサネット 5
    
[/code]

## ソースコード改訂

[code]

    import json
    import locale
    import subprocess
    
    cmd_list = ['systeminfo', '/fo', 'LIST']
    
    cmdreturn = subprocess.run(
        cmd_list, check=True, stdout=subprocess.PIPE)
    
    local_encode = locale.getdefaultlocale()
    if local_encode:
        language_code, encoding = local_encode
    
        retcode = cmdreturn.returncode
        retout = cmdreturn.stdout
    
        with open('systeminfo.bin','bw') as f:
            f.write(retout)
        #textout = retout.decode()
        try:
            textout = retout.decode(encoding)
        except Exception as e:
            print(e)
            raise e
    
        text_list = textout.splitlines()
    
    
        param_dict = dict()
        c_key = None
    
        for text_line in text_list:
            if not text_line.startswith(' '):
                if ':' in text_line:
                    key_word = text_line.split(':', maxsplit=1)
                    if key_word[1].startswith(' '):
                        key_word[1] = key_word[1].lstrip()
                    param_dict.setdefault(key_word[0], key_word[1])
                    c_key = key_word[0]
            else:
                temp = text_line.lstrip()
                if '\t' in temp:
                    temp = temp.split('\t')
                    temp = ''.join(temp)
                if c_key in param_dict:
                    c_data = param_dict[c_key]
                    if isinstance(c_data, list):
                        set_list = c_data
                        set_list.append(temp)
                        pass
                    else:
                        set_list = [c_data, temp]
                        param_dict[c_key] = set_list
        else:
            ret_dump = json.dumps(param_dict, indent=2, ensure_ascii=False)
    
            print(ret_dump)
    
    
[/code]

## 発生する例外

[code]

    UnicodeDecodeError: 'cp932' codec can't decode byte 0x97 in position 2475: illegal multibyte sequence
    
[/code]

## バイナリエディタ

バイナリを出力してエディタで表示した結果画像を貼り付けます。  
ちなみに、PC2では該当部分に0x97というバイトは存在しません。

[![バイナリエディタ表示](https://i.stack.imgur.com/hj46j.png)](https://i.stack.imgur.com/hj46j.png)

# 再追加試験

コマンドプロンプトで、以下のコマンドを実行

[code]

    systeminfo /fo LIST > output.txt
    
[/code]

この結果を以下のPythonスクリプトでバイナリ化

[code]

    with open('output.txt','br') as f:
        txt = f.read()
    
    with open('output.bin','bw') as f:
        f.write(txt)
    
[/code]

追加試験時とのバイナリ比較をVScode上で表示  
[![バイナリ比較](https://i.stack.imgur.com/aXnAm.png)](https://i.stack.imgur.com/aXnAm.png)

subprocessモジュールによるstdoutでは日本語出力がうまく実行できないと考えてよいのでしょうか？

さらにsubprocessモジュールのencoding引数に、localeモジュールで取得したencodingを指定すると、0x97で引っかかって例外出力されます。cp932,utf-8,shift-
jisを試しましたが結果が同じでした。


CC BY-SA 4.0
2022-10-11T02:28:26.927
91540
2022-10-12T08:49:27.607
2022-10-12T06:33:42.340
32891
32891
question
python python3 windows 文字コード
Windows　文字コードに関するエラー


日本語環境以外では当然ShiftJIS/CP932以外もあり得ますし、日本語環境でもWindows
10以降ではUTF-8/CP65001になっている場合もあります。  
Pythonのバージョン、Windowsのコードページ設定、環境変数のPYTHONIOENCODINGやPYTHONUTF8などを調べるのはどうでしょうか？

追加分についてSJISやCP932の第2バイトとして3Fは未使用領域のはずなのに3F 97
3Fという出力になってるので、何かsysteminfoが不適切な出力をする条件があるんだと思います。

<https://ja.wikipedia.org/wiki/Microsoft%E3%82%B3%E3%83%BC%E3%83%89%E3%83%9A%E3%83%BC%E3%82%B8932>

エラーになりさえしなければ良いという用途であれば `textout = retout.decode('shift-jis',
errors='replace')` のような対応はできますが当然情報は失われます。


CC BY-SA 4.0
2022-10-11T04:01:42.030
91541
2022-10-12T08:49:27.607
2022-10-12T08:49:27.607
241
241
91540
answer