.NETコンソール・アプリの文字エンコーディングはデフォルト設定にした方が良い

2020年11月22日日曜日

システム開発

t f B! P L


shift-jis と utf-8 の混在問題に関する記事(リンクリスト)に戻る


これまで、「shift-jis と utf-8 の混在問題」を扱ってきたが、特に .NET5.0 においてWCFが廃止されたことにより、Windows 環境の .NETアプリで扱うテキストデータも Shift-JIS から UTF-8 に変更する必要性が増してきたと思う。

これまでも、レガシー資産が Shift-JIS の場合は Shift-JIS と UTF-8 が共存できる体制を作って、UTF-8 メインのシステム体制へ移行すべきだと解説してきた。

その為の注意点とノウハウと必要と思われるサンプルコードなど記事に書いてきた。

 

今回は、コンソール・アプリのコマンドメッセージ表示とコンソール入力に使用する文字エンコーディングはどうすべきか検証した。

 

検証対象としたソフトウェアは以下の物である。

 

cmd (コマンドプロンプト)

PowerShell version5.1

PowerShell version7.1

WSL2 Ubuntu18.04 bash

 

.NET Framework 4.6

.NET Core 3.1

.NET 5.0

 

昔の Windows は cmd (コマンドプロンプト)だけでコンソール・アプリを稼働していたので、cmdの事だけ考えれば良かった。

また、昔は Shift-JIS しか使用していなかったので、コンソール・アプリの表示と入力に使用する文字エンコーディングを意識する必要はなかった。

 

現在の Windows 10 は、主なコンソールが PowerShell に置き換えられ、 cmd (コマンドプロンプト) は旧式ソフトウェアとの互換性維持の為に残されている予備のシェルとなっている。

また、Windows 10 環境で Linux 系OSSが使用できるように WSL が導入され、現在はバージョン2 の WSL2 となっている。

WSL2 の採用している Linuxディストリビューションは全て、文字エンコーディングを UTF-8 に統一している。

 

今後、システム開発でバッチやツールなどのコンソール・アプリを開発する場合は、cmd と PowerShell のどちらでも動作するように開発しなければならない。

また、.NET Core を使用する場合は Linux でも動作するように開発しなければならない場合もあると思う。

 

このコンソールが複数規格存在する状態で、文字エンコーディングを Shift-JIS と UTF-8 が共存できるようにするのは、結構面倒な作業に思え、色々試してみた。

 

コンソール文字エンコーディング検証の結果

先に検証の結果から報告すると、意外に簡単な結論になった。

「コンソール・アプリの表示と入力用の文字エンコーディングはデフォルト設定に任せるべき」

これは、cmd でも PowerShell でも WSL2 の Ubuntu の bash でも全てにおいて、成立する。

サンプルソースは以下になる。

using System;
using System.Text;

namespace ConsoleTestDotnet50
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Abc123-!#$%;-日本漢字.欄廊俠俱");
            string inputText = Console.ReadLine();
            Console.WriteLine(inputText);
        }
    }
}

namespace は何でも良い。

たまたま、これは .NET 5.0 の検証に使用したコードなので「ConsoleTestDotnet50」になっているが、

.NET 4.6 でも .NET Core 3.1 でもこれと同じソースコードで問題なく動作する。

Visual Studio の C# のソースコードは UTF-8 なので、Console.WriteLine で表示する文字エンコーディングも UTF-8 になる。

「日本漢字.欄廊俠俱」という漢字の「日本漢字」は常用漢字なので Shift-JIS でも表せるが、「欄廊俠俱」は UTF-8 で表せるが、Shift-JIS では表せない漢字である。

「欄廊俠俱」を Shift-JIS のコンソールで表すと文字化けする。

 

Windows 10 のコンソールはデフォルトでは Shift-JIS

cmd も PowerShell もコンソールが扱うデフォルト設定の文字エンコーディングは Shift-JIS である。

WSL2 の Ubuntu の bash だけが、UTF-8 で表示と入力を行う。

当然の事ながらデフォルト設定のまま cmd と PowerShell でテストプログラムを実行すると、「日本漢字.欄廊俠俱」の「欄廊俠俱」の部分が文字化けして表示できない。

正確には「?」に置き換えられて「日本漢字.????」と表示される。

実行結果は以下のようになる。

Abc123-!#$%;-日本漢字.????
漢字.欄廊
漢字.??

Console.ReadLine() の要求する入力には「漢字.欄廊」を入力した。

その入力値をそのまま表示すると「欄廊」が Shift-JIS で表せないので、「漢字.??」と表示される。

 

普通に考えたら UTF-8 で表せる文字が全て表せないのは良くないと考えると思う。

 

しかし、検証の結果、cmd と PowerShell の 表示(Console.WriteLine)と入力(Console.ReadLine)のどちらでも正常に UTF-8 を入出力できる方法は無かった。

唯一、 UTF-8 が正しく入出力できたのは .NET Core で WSL2 の Ubuntu の bash 上で動作した時だけだ。

 

cmd と PowerShell v7.1 の 表示(Console.WriteLine)だけなら、正常に「日本漢字.欄廊俠俱」を表示させる事ができる。

しかし、その場合、入力(Console.ReadLine)の方が漢字を正しく入力できなくなってしまう。

英数字は入力できるが、「欄廊俠俱」だけではなく、常用漢字の「日本漢字」も入力できない。

 

また、 PowerShell v5.1 では UTF-8 を正常に表示する事ができなかった。

漢字は全て □ で置き換えられてしまう。

 

以上の検証結果から、Windows10 のコンソール・アプリでは表示と入力の文字エンコーディングはデフォルト設定のまま Shift-JIS で行うのが一番実用的であると考える。

 

chcp コマンドによる文字エンコーディングの変更

cmd も PowerShell も表示用の文字エンコーディングは「chcp」コマンドでコードページを指定する事で変更する事ができる。

Shift-JIS のコードページは「932」、UTF-8 のコードページは「65001」である。

コンソールで、

chcp 65001

と打ち込めば UTF-8 表示に切り替わり、

chcp 932

と打ち込めば Shift-JIS 表示に切り替わる。

引数なしで入力すると現在のコードページを表示する。

chcp

つまり、cmd も PowerShell も Shift-JIS と UTF-8 のどちらのメッセージも表示する機能が備わっている。

 

しかし、入力は Shift-JIS だけしか受け付けず、「chcp 65001」モードでは漢字入力を受け付けない。

これが第一のデフォルト設定の Shift-JIS で表示すべき理由だ。

 

意外に厄介な PowerShell v5.1 の存在

Windows 10 , Windows Server 2016 , Windows Server 2019 には、PowerShell v5.1 が既定でインストールされている。

PowerShell の最新バージョンは v7.1 だが、これはユーザーが自らインストールしなければ使用できない。

そして、厄介なことに PowerShell v7.1 をインストールしても、 PowerShell v5.1 はアップグレードする事はない。

PowerShell v7.1 と、 PowerShell v5.1 が、互いに独立して「共存」してしまうのだ。

つまり両方使えてしまう。

PowerShell v5.1 は、UTF-8 の漢字を表示できない。

Shift-JIS だけである。

この PowerShell v5.1 はユーザーの端末から消滅する事がない。

だから、特に PowerShell のバージョンを気にしないユーザーの場合は、既定でインストールされている PowerShell v5.1 をそのまま使ってしまう。

 

これが第二のデフォルト設定の Shift-JIS で表示すべき理由だ。

 

ちなみに PowerShell v5.1 ではコマンドレッドのオプションに「-Encoding "shift_jis"」が使えない為、コマンドレッドで Shift-JIS のテキストファイルを読むことができない。

例えば以下のコマンドでは Shift-JIS のテキストファイルが無視されてしまう。

Get-ChildItem -Path ".\*.txt" -Recurse | Select-String -Pattern '国債発行' 

だが、これは .NETコンソール・アプリとは関係ない。

 

.NETアプリは UTF-8 を出力するが、コンソールは Shift-JIS に変換する

既にお気づきかも知れないが、.NETコンソール・アプリはソースコードも UTF-8 で書かれており、表示(Console.WriteLine)も UTF-8 で出力する。

.NET 4.6 .NET Core 3.1 .NET 5.0 のどれもその点は同じである。

 

しかし、cmd と PowerShell は自動的に UTF-8 の出力を Shift-JIS に変換する。

本来は UTF-8 のコード番号と Shift-JIS のコード番号は異なるので、同じ常用漢字の「日本漢字」であっても、文字化けするはずなのだが、コンソールに表示すると正常に「日本漢字」と表示する。

これは cmd と PowerShell が、常用漢字を自動的に UTF-8 から Shift-JIS に変換してからコンソールに表示している証拠である。

つまり、 UTF-8 でコンソール・アプリの文字列を扱っていても、コンソールに表示した時は常用漢字ならば正常に Shift-JIS に変換して表示してくれるのだ。

常用漢字意外の Shift-JIS で表せない文字は「?」で置き換えられてしまう。

しかし、常用漢字が表示できるのなら、実用上は問題なく使用できる。

これが第三のデフォルト設定の Shift-JIS で表示すべき理由だ。

 

.NET Core アプリは Linux上では自動的に UTF-8 で動作する

.NET Core 3.1 と .NET 5.0 のどちらも、WSL2の Ubuntu18.04 の bash で動作させると、正常に UTF-8 で表示も入力もできた。

実行結果は以下になる。

$ dotnet run
Abc123-!#$%;-日本漢字.欄廊俠俱
日本漢字.欄廊俠俱
日本漢字.欄廊俠俱

これが第四のデフォルト設定 で表示すべき理由だ。

デフォルト設定ならLinuxでは自動的に UTF-8 になる。

 

まとめ

第一の理由「UTF-8 モードにすると常用漢字も入力できなくなってしまう」

chcp 65001 コマンドで UTF-8 モードにすると常用漢字すら Console.Readline() で入力できなくなってしまう。

 

第二の理由「PowerShell v5.1 は、UTF-8 の漢字を表示できない」

既定でインストールされている PowerShell v5.1 は、v7.1インストール後も共存してしまうので、ユーザーが v5.1 を使ってしまう事を防ぐのが難しい。

そして PowerShell v5.1 は UTF-8 の漢字を表示できない。

 

第三の理由「コンソールは自動的に UTF-8 から Shift-JIS に変換してくれる」

コンソール・アプリを UTF-8 文字列で開発していても、それをコンソールに表示すると、コンソールが自動的に Shift-JIS に変換してくれる。

よって、わざわざ Shift-JIS に変換する必要がない。

ただし、 Shift-JIS で扱えない文字は「?」に置き換えられてしまい読めない。

使えるのは英数字と標準記号と常用漢字だけである。

この点は cmd も PowerShell も同様である。

 

第四の理由「デフォルト設定ならLinuxでは自動的に UTF-8 で動作する」

.NET Core 3.1 と .NET 5.0 は、Linux でも動作する。

デフォルト設定 で作成したコンソール・アプリを Linux の bash で稼働すると、自動的に UTF-8 で表示も入力もしてくれる。

もちろん常用漢字意外の漢字も使用できる。

 

以上が報告の全てである。

 

再検証用の資料

読者もこの検証が正しいか再検証したいかも知れないので、説明していない検証の資料を以下に記載する。

UTF-8 で表示する

cmd や PowerShell コンソールで UTF-8 で表示する時は以下のようにする。

        static void Main(string[] args)
        {
            Console.OutputEncoding = Encoding.UTF8;
            //Console.OutputEncoding = Encoding.Default;
            //Console.InputEncoding = Encoding.UTF8;
            Console.InputEncoding = Encoding.Default;

            Console.WriteLine("Abc123-!#$%;-日本漢字.欄廊俠俱");
            string inputText = Console.ReadLine();
            Console.WriteLine(inputText);
        }

「Console.OutputEncoding = Encoding.UTF8」で、 UTF-8 で表示する事を指定する。

「chcp 932」の状態で、「Console.OutputEncoding = Encoding.Default」と書くことで、Shift-JIS で表示する事を指定する。

「Console.InputEncoding = Encoding.UTF8」は入力文字エンコーディングの指定である。

cmd と PowerShell v7.1 で表示はできるが、入力はできない。

PowerShell v5.1 では表示もできない。

 

System.Text.Encoding.CodePages を使用する

NuGetから System.Text.Encoding.CodePages をインストールする。

以下のコードを先頭に記述する。

Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

例、

        static void Main(string[] args)
        {
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            //Console.OutputEncoding = Encoding.UTF8;
            //Console.OutputEncoding = Encoding.Default;
            //Console.InputEncoding = Encoding.UTF8;
            //Console.InputEncoding = Encoding.Default;

            Console.WriteLine("Abc123-!#$%;-日本漢字.欄廊俠俱");
            string inputText = Console.ReadLine();
            Console.WriteLine(inputText);
        }

これは .NET Core で、Shift-JIS を使えるようにする処置なのだが、コンソールは UTF-8 の文字を自動的に Shift-JIS に変換するので、.NET Core のアプリの中では Shift-JIS を使用する必要がない。

よって、この処置は必要無くなった。

これが必要になるのは、.NET Core のアプリの中で Shift-JIS の文字列を扱う場合である。

Shift-JIS のテキストファイルを読み書きする場合などが該当する。

 

以上、ご自分で試して見て欲しい。

 

この資料がお役に立てば幸いだ。


こちらも参考までにどうぞ、

powershell v7.1のインストール とバージョン管理の注意点



shift-jis と utf-8 の混在問題に関する記事(リンクリスト)に戻る


このブログを検索

Translate

人気の投稿

自己紹介

自分の写真
オッサンです。実務経験は Windows環境にて C#,VB.NET ,SQL Server T-SQL,Oracle PL/SQL,PostgreSQL,MariaDB。昔はDelphi,C,C++ など。 趣味はUbuntu,PHP,PostgreSQL,MariaDBかな ?基本無料のやつ。

QooQ