WebSurfer's Home

Filter by APML

.NET アプリで Shift_JIS を使うには?

by WebSurfer 14. March 2025 13:57

.NET アプリにおいて Encoding.GetEncoding メソッドで Shift_JIS のエンコーディングを取得しようとすると、以下の画像のように ArgumentException 例外がスローされます。

Shift_JIS エンコーディングの取得

これは、.NET では特定の OS に依存する機能はサポートされてないので、Shift_JIS に限らず Windows OS のコードページの中の特定の locale のエンコーディングはデフォルトでは使えないということのようです。(注: .NET Framework ではデフォルトで Windows コードページの大多数のエンコーディングが使えます)

サポートされているエンコーディングは Encoding.GetEncodings メソッドで取得できます。.NET 8.0 の場合、自分の環境で試すと以下の 7 種類になっていました。

  • ASCII (code page 20127)
  • UTF-8 (code page 65001)
  • UTF-16LE (code page 1200)
  • UTF-16BE (code page 1201)
  • UTF-32LE (code page 12000)
  • UTF-32BE (code page 12001)
  • ISO-8859-1 (code page 28591)

UTF-7 も含まれるというMicrosoft のドキュメントを目にしましたが "The UTF-7 encoding is insecure and should not be used." ということで除外されたようです。

では、.NET アプリで上の一覧にないエンコーディングを Encoding.GetEncoding メソッドで取得して使うにはどうするかですが、それについては Microsoft のドキュメント「EncodingProvider クラス」に説明があります。それを参考に以下に Shift_JIS を使う場合の例を書きます。

(1) プロバイダクラスを定義

抽象クラス EncodingProvider を継承したプロバイダクラスを定義します。この例では、プロバイダクラスの名前を ShiftJisEncodingProvider とします。

ShiftJisEncodingProvider クラスで、 継承した抽象クラス EncodingProvider の抽象メソッド GetEncoding(Int32) と GetEncoding(String) を override し、Shift_JIS エンコーディングを返すようにします。

Shift_JIS エンコーディングは CodePagesEncodingProvider クラスのインスタンスから取得します。

ShiftJisEncodingProvider クラスのコード例は以下の通りです。

using System.Text;

public class ShiftJisEncodingProvider : EncodingProvider
{
    public override Encoding? GetEncoding(int codepage)
    {
        var provider = CodePagesEncodingProvider.Instance;
        if (codepage == 932)
        {            
            return provider.GetEncoding(932);
        }

        return null;
    }

    public override Encoding? GetEncoding(string name)
    {
        var provider = CodePagesEncodingProvider.Instance;
        if (name.Equals("shift_jis", StringComparison.CurrentCultureIgnoreCase))
        {                
            return provider.GetEncoding("shift_jis");
        }
        
        return null;
    }
}

(2) ShiftJisEncodingProvider の登録

ShiftJisEncodingProvider のインスタンスを Encoding.RegisterProvider メソッド に渡して、.NET アプリで Shift_JIS エンコーディングを取得できるようにします。

(3) Shift_JIS エンコーディングの取得

.NET アプリでは Encoding.GetEncoding メソッドを使って Shift_JIS エンコーディングを取得します。ステップ (2) と (3) のコード例は以下の通りです。コメントの結果に示すように期待通り Shift_JIS エンコーディングが取得できています。

using System.Text;

namespace ConsoleApp4
{
    internal class Program
    {
        static void Main(string[] args)
        {
            var provider = new ShiftJisEncodingProvider();
            Encoding.RegisterProvider(provider);

            var sjis = Encoding.GetEncoding("shift_jis");
            Console.WriteLine($"{sjis.EncodingName}\t{sjis.CodePage}\t{sjis.WebName}");

            var str = "123abcあいう";
            Console.Write($"{str}: ");

            byte[] bytes = sjis.GetBytes(str);
            foreach (byte b in bytes)
            {
                Console.Write($"[{b:x2}]");
            }            
        }
    }
}

// 結果:
// Japanese (Shift-JIS)    932     shift_jis
// 123abcあいう: [31][32][33][61][62][63][82][a0][82][a2][82][a4]

上で述べたようなカスタムプロバイダクラスを定義しなくても Windows コードページのエンコーディングを簡単に全部使えるようにする方法もあります。

その方法は、Microsoft のドキュメント「CodePagesEncodingProvider クラス」に説明があります。

  1. CodePagesEncodingProvider.Instance 静的プロパティから CodePagesEncodingProvider オブジェクトを取得します。
  2. CodePagesEncodingProvider オブジェクトを Encoding.RegisterProvider メソッドに渡します。

コード例は以下の通りで一行で済みます。必要ないエンコーディングまで使えるようにすることで何か予期せぬ副作用が出るかもしれないという可能性は否定しきれませんが、こちらのが本筋かもしれません。

using System.Text;

namespace ConsoleApp4
{
    internal class Program
    {
        static void Main(string[] args)
        {
            // 以下の一行で OK
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

            var sjis2 = Encoding.GetEncoding("shift_jis");
            Console.WriteLine($"{sjis2.EncodingName}\t{sjis2.CodePage}\t{sjis2.WebName}");

            Console.Write($"{str}: ");

            byte[] bytes2 = sjis2.GetBytes(str);
            foreach (byte b in bytes2)
            {
                Console.Write($"[{b:x2}]");
            }

            Console.WriteLine("\n\nサポートしているすべてのエンコーディングを取得する");
            EncodingInfo[] eis = Encoding.GetEncodings();
            foreach (EncodingInfo ei in eis)
            {
                Console.WriteLine($"{ei.DisplayName}\t{ei.CodePage}\t{ei.Name}");
            }
        }
    }
}

/* 結果
Japanese (Shift-JIS)    932     shift_jis
123abcあいう: [31][32][33][61][62][63][82][a0][82][a2][82][a4]

サポートしているすべてのエンコーディングを取得する
Japanese (Shift-JIS)    932     shift_jis
Portuguese (DOS)        860     IBM860
Icelandic (DOS) 861     ibm861
IBM EBCDIC (Cyrillic Russian)   20880   IBM880
Hebrew (DOS)    862     DOS-862
French Canadian (DOS)   863     IBM863
Chinese Simplified (GB2312)     936     gb2312
Arabic (864)    864     IBM864
Nordic (DOS)    865     IBM865
Cyrillic (DOS)  866     cp866
Cyrillic (KOI8-U)       21866   koi8-u
IBM EBCDIC (US-Canada)  37      IBM037
Greek, Modern (DOS)     869     ibm869
IBM EBCDIC (International)      500     IBM500
Icelandic (Mac) 10079   x-mac-icelandic
IBM EBCDIC (US-Canada-Euro)     1140    IBM01140
IBM EBCDIC (Germany-Euro)       1141    IBM01141
IBM EBCDIC (Denmark-Norway-Euro)        1142    IBM01142
IBM EBCDIC (Germany)    20273   IBM273
IBM EBCDIC (Finland-Sweden-Euro)        1143    IBM01143
IBM EBCDIC (Italy-Euro) 1144    IBM01144
IBM EBCDIC (Spain-Euro) 1145    IBM01145
Central European (Windows)      1250    windows-1250
IBM EBCDIC (UK-Euro)    1146    IBM01146
Cyrillic (Windows)      1251    windows-1251
IBM EBCDIC (France-Euro)        1147    IBM01147
Western European (Mac)  10000   macintosh
Western European (Windows)      1252    windows-1252
Arabic (DOS)    720     DOS-720
IBM EBCDIC (Denmark-Norway)     20277   IBM277
IBM EBCDIC (International-Euro) 1148    IBM01148
Japanese (Mac)  10001   x-mac-japanese
Greek (Windows) 1253    windows-1253
OEM United States       437     IBM437
IBM EBCDIC (Finland-Sweden)     20278   IBM278
IBM EBCDIC (Icelandic-Euro)     1149    IBM01149
Chinese Traditional (Mac)       10002   x-mac-chinesetrad
Turkish (Windows)       1254    windows-1254
Hebrew (Windows)        1255    windows-1255
Korean (Johab)  1361    Johab
Arabic (Windows)        1256    windows-1256
Arabic (Mac)    10004   x-mac-arabic
Baltic (Windows)        1257    windows-1257
Hebrew (Mac)    10005   x-mac-hebrew
Vietnamese (Windows)    1258    windows-1258
Greek (Mac)     10006   x-mac-greek
Cyrillic (Mac)  10007   x-mac-cyrillic
IBM Latin-1     20924   IBM00924
Central European (ISO)  28592   iso-8859-2
Latin 3 (ISO)   28593   iso-8859-3
Baltic (ISO)    28594   iso-8859-4
Cyrillic (ISO)  28595   iso-8859-5
Arabic (ISO)    28596   iso-8859-6
IBM EBCDIC (Multilingual Latin-2)       870     IBM870
Greek (ISO)     28597   iso-8859-7
Hebrew (ISO-Visual)     28598   iso-8859-8
Turkish (ISO)   28599   iso-8859-9
Turkish (Mac)   10081   x-mac-turkish
Croatian (Mac)  10082   x-mac-croatian
Thai (Windows)  874     windows-874
IBM EBCDIC (Greek Modern)       875     cp875
IBM EBCDIC (Arabic)     20420   IBM420
Korean  949     ks_c_5601-1987
IBM EBCDIC (Greek)      20423   IBM423
IBM EBCDIC (Hebrew)     20424   IBM424
IBM EBCDIC (Italy)      20280   IBM280
IBM Latin-1     1047    IBM01047
IBM EBCDIC (Spain)      20284   IBM284
IBM EBCDIC (UK) 20285   IBM285
Romanian (Mac)  10010   x-mac-romanian
Japanese (JIS 0208-1990 and 0212-1990)  20932   EUC-JP
Ukrainian (Mac) 10017   x-mac-ukrainian
Europa  29001   x-Europa
Greek (DOS)     737     ibm737
Western European (IA5)  20105   x-IA5
Chinese Traditional (Big5)      950     big5
Chinese Simplified (GB2312-80)  20936   x-cp20936
German (IA5)    20106   x-IA5-German
Swedish (IA5)   20107   x-IA5-Swedish
Norwegian (IA5) 20108   x-IA5-Norwegian
Cyrillic (KOI8-R)       20866   koi8-r
Baltic (DOS)    775     ibm775
Estonian (ISO)  28603   iso-8859-13
IBM EBCDIC (Japanese katakana)  20290   IBM290
Latin 9 (ISO)   28605   iso-8859-15
Chinese Traditional (CNS)       20000   x-Chinese-CNS
Arabic (ASMO 708)       708     ASMO-708
IBM EBCDIC (France)     20297   IBM297
Thai (Mac)      10021   x-mac-thai
TCA Taiwan      20001   x-cp20001
IBM EBCDIC (Turkish)    20905   IBM905
Chinese Traditional (Eten)      20002   x-Chinese-Eten
IBM EBCDIC (Korean Extended)    20833   x-ebcdic-koreanextended
IBM5550 Taiwan  20003   x-cp20003
TeleText Taiwan 20004   x-cp20004
Wang Taiwan     20005   x-cp20005
Western European (DOS)  850     ibm850
IBM EBCDIC (Thai)       20838   IBM-Thai
Central European (DOS)  852     ibm852
IBM EBCDIC (Icelandic)  20871   IBM871
Central European (Mac)  10029   x-mac-ce
OEM Cyrillic    855     IBM855
IBM EBCDIC (Cyrillic Serbian-Bulgarian) 21025   cp1025
Korean Wansung  20949   x-cp20949
Turkish (DOS)   857     ibm857
OEM Multilingual Latin I        858     IBM00858
T.61    20261   x-cp20261
IBM EBCDIC (Turkish Latin-5)    1026    IBM1026
ISO-6937        20269   x-cp20269
Unicode 1200    utf-16
Unicode (Big-Endian)    1201    utf-16BE
Unicode (UTF-32)        12000   utf-32
Unicode (UTF-32 Big-Endian)     12001   utf-32BE
US-ASCII        20127   us-ascii
Western European (ISO)  28591   iso-8859-1
Unicode (UTF-8) 65001   utf-8
*/

Tags: , , ,

CORE

React で日本語を使うとコンパイルエラー / 文字化け

by WebSurfer 10. April 2022 14:26

Visual Studio 2019 / 2022 の「React.js での ASP.NET Core」のテンプレートで作ったプロジェクトで、自動生成された Home.js とか Counter.js などに以下のように <p>日本語</p> という日本語を書き加えると Unexpected character '�' というエラーメッセージが出てコンパイルエラーになります。文字によってはコンパイルは通りますが文字化けします。

Home.js に日本語追加

フレームワークを .NET Core 3.1 としてプロジェクトを作って同様なことをすると、ブラウザに空白画面が表示されるだけなので何が起こったのか分からないと思いますが、.NET 6.0 とするとコマンドラインに以下のように表示されます。(コンパイルエラーにならない場合は文字化けしてブラウザに表示されるので分かります)

コマンドラインのエラーメッセージ

ブラウザにも以下のようにエラーメッセージが表示されます。

ブラウザのエラーメッセージ

文字によってはコンパイルは通り、ブラウザには結果が表示されますが、日本語の部分は文字化けします。 (コンパイルが通るもの / 通らないものの違いは不明です)

原因は Visual Studio で編集して保存する時ファイルの文字コードが Shift_JIS になってしまうからです。下の画像は Home.js をバイナリエディタで表示したものですが「日本語」の部分が 93 FA 96 78 8C EA と Shift_JIS になっているのが分かるでしょうか。

バイナリエディタで表示

何故そんなことになってしまうかというと、Visual Studio の設定がそうなっているからです。下の画像を見てください。Home.js の「保存オプションの詳細設定」ですが、そこで[エンコード(E)]が「日本語 (シフト JIS) - コードページ 932」になっているのに注目してください。

保存オプションの詳細設定

このため日本語を書き込んで保存すると Shift_JIS になってしまい、UTF-8 を期待している React ではコンパイルエラーまたは文字化けするということです。Home.js 以外にも ClientApp/src フォルダのほとんどのファイルがその設定になっていますので注意してください。

解決策は、下の画像のように[エンコード(E)]を「Unicode (UTF-8 シグネチャなし) - コードページ 65001」として上書きすることです。

保存オプションの詳細設定

または、メモ帳で問題の .js ファイルを開いて[文字コード(E)]を BOM なし UTF-8 に設定して上書きすることでも OK です。一旦メモ帳で BOM なし UTF-8 で保存してしまえば、その後は Visual Studio で .js ファイルを開いて日本語を書き込んでも UTF-8 で保存されるのでこの問題は起こりません。(詳しい仕組みは分かりませんが、Visual Studio の「保存オプションの詳細設定」設定に反映されるようです)

なお、BOM 付き UTF-8 にすると下の画像のように Unexpected Unicode BOM (Byte Order Mark) という警告が出ますので注意してください。(「保存オプションの詳細設定」でシグネチャ付きというのは BOM 付きのことですので注意)

BOM による警告

BOM 付きでもコンパイルエラーにはならず文字化けもしませんが、そこは警告に従って BOM なし UTF-8 にするのが良さそうです。

Visual Studio を操作して[追加(D)]⇒[新しい項目(W)...]で「JavaScript ファイル」を選んで作成すると BOM 付き UTF-8 になることに注意してください。コンパイルは通りますが、やはり Unexpected Unicode BOM (Byte Order Mark) という警告が出ます。

React プロジェクトでは ClientApp/src フォルダの .js ファイルだけでなく、Program.cs や Startup.cs でも保存オプションはデフォルトで「日本語 (シフト JIS) - コードページ 932」になっています。そこは ASP.NET Core プロジェクトでも同じです。

Program.cs や Startup.cs は Visual Studio が読んで処理を行いますので正しく文字コードが解釈され Shift_JIS でも問題は出ないはずです。ただ、ちょっと特殊な話ですが問題事例はありました。興味がありましたらTeratail のスレッド「asp.net.coreでの文字化けを解決したい。」を見てください。これはブラウザに送信する際は UTF-8 になっていたが、応答ヘッダやメタタグで文字コードの指定をしてなかったので、ブラウザが Shif_JIS として処理して文字化けを起こしたというもので、開発者のミスに近いものですが。

この記事の React の .js ファイルの話は、昔々 .NET Core 1 時代の MVC アプリで経験した「ASP.NET Core で文字化け」の .cshtml ファイルの件と似た話です。Microsoft も文字コードの問題はなかなか気が付かないのでしょうね。

Tags: , , , ,

React

Web API に XML データを送信

by WebSurfer 9. August 2020 15:08

ASP.NET Web API 2 に XML 形式のデータを送信し、サーバー側のアクションメソッドで受け取る方法を書きます。(注: .NET Framework 版の話です。Core 版は未検証・未確認)

UTF-8 の場合

ASP.NET Web API で XML データを送信するというのは需要がなさそうで、あまり役には立たないかもしれませんが、せっかく考えたことなので整理して備忘録として残しておくことにしました。

(1) UTF-8 の場合

元の話は Teratail のスレッド「ASP.NET Web APIでPOSTされたXMLデータをXML形式で取得する方法」のものです。

サンプル XML データは以下の通りです。

<?xml version="1.0" encoding="utf-8"?>
<sample code="1.0">
  <data>
   <id>100</id>
  </data>
  <value>
    <name>日本語</name>
    <version>1.0.0</version>
  </value>
</sample>

この XML データを ASP.NET Web API に POST 送信し、サーバー側でアクションメソッド Post(SampleData value) の引数 value にモデルバインドするにはどうしたらよいかという話です。SampleData クラスの定義は以下の通り。属性を付与した理由は下に書きます。

namespace WebApi.Models
{
    [System.Xml.Serialization.XmlRoot("sample")]
    public class SampleData
    {
        [System.Xml.Serialization.XmlAttribute("code")]
        public string Code { get; set; }

        [System.Xml.Serialization.XmlElement("data")]
        public Data Data { get; set; }

        [System.Xml.Serialization.XmlElement("value")]
        public Value Value { get; set; }
    }

    public class Data
    {
        [System.Xml.Serialization.XmlElement("id")]
        public string Id { get; set; }
    }

    public class Value
    {
        [System.Xml.Serialization.XmlElement("name")]
        public string Name { get; set; }

        [System.Xml.Serialization.XmlElement("version")]
        public string Version { get; set; }
    }
}

XML データの文字コードが UTF-8 であれば以下の 3 つの設定でこの記事の一番上の画像の通りモデルバインドできます。

  1. 要求ヘッダに Content-Type: application/xml; charset=UTF-8 の設定

    Microsoft のドキュメント How WebAPI does Parameter Binding には content-type の設定だけで済みそうなことが書いてありますが、自分が試した限りでは下の 2, 3 も必要でした。
  2. App_Start/WebApiConfig.cs へ以下の設定の追加

    config.Formatters.XmlFormatter.UseXmlSerializer = true;  
  3. モデルのクラス、プロパティに XmlRoot, XmlAttribute, XmlElement 属性の付与

    xml コードの要素名とそれをバインドするクラス、プロパティ名を関連付けるために必要です。たとえ同名でも、上の例のように大文字小文字の違いがある場合は、プロパティにXmlElement 属性を付与してその引数に xml の要素名を設定しないとバインドされません。また、xml コードの属性値(この記事の例では code)をバインドする場合は、それに該当するプロパティへの XmlAttribute 属性の付与が必要です。

(2) Shift_JIS の場合

XML データの文字コードが Shift_JIS の場合はどうでしょう? (これも Teratail のスレッド「ASP.NET Web API POSTされた文字エンコードShift_JISのXMLデータをモデルバインドできない」の話です)

上の「(1) UTF-8 の場合」のような方法ではうまくいかないようです(null がバインドされる)。もちろん XML データを encoding="shift_jis" とし、要求ヘッダを Content-Type: application/xml; charset=shift_jis としたのですがダメでした。

検索するなどして調べてみましたが成果がなかったです。Shift_JIS ではヒットしないので UTF-16 に範囲を広げてググってみましたが、stackoverflow のスレッド Web API not able to bind model for POST with utf-16 encoded XML など、できなかったという記事しか見つかりませんでした。

そこを何とかするには、Microsoft のドキュメント How WebAPI does Parameter Binding の最初の方に書いてあるように、HttpRequestMessage クラスを引数に持つメソッドを使う他には手がなさそうな感じです。

HttpRequestMessage.Content プロパティで HttpContent オブジェクトを取得し、HttpContent.ReadAsStringAsync メソッドで POST されてきた XML 文字列を読んで、XDocument.Load メソッドで XDocument オブジェクトを作ってそれを操作するようにしてみました。

なお、この方法を取った場合、上の「UTF-8 の場合」で行った「2. App_Start/WebApiConfig.cs へ以下の設定の追加」と「3. モデルのクラス、プロパティに XmlRoot, XmlAttribute, XmlElement 属性の付与」は必要ありません。

検証は以下のようにしました。

まず Fiddler の Composer を使って、要求ヘッダに Content-Type: application/xml; charset=shift_jis を付与し、Request Body に XML データを設定して送信します。

Fiddler の Composer で送信

Fiddler の Inspectors で HexView を選択し、Request Body に設定して送信した XML データの 16 進数表現をチェックします。XML データの中の <name>日本語</name> の「日本語」の文字コードは 93 FA 96 7B 8C EA と Shift_JIS になっているのが分かります。

要求ヘッダとボディの 16 進数データ

Visual Studio のデバッガでアクションメソッドの Local 変数を確認します。「日本語」は文字化けせず期待通り取得できています。

アクションメソッドの Local 変数

ちょっと予想外だったのは、Fiddler の Composer の Request Body に書いた「日本語」の文字コードが Shift_JIS になることと、HttpContent.ReadAsStringAsync メソッドがそれを正しく「日本語」と読んでくれることです。そんなに都合よくできているというのが信じ難く、何か見落としがあるかも。(汗)

Tags: , , ,

Web API

About this blog

2010年5月にこのブログを立ち上げました。主に ASP.NET Web アプリ関係の記事です。ブログ2はそれ以外の日々の出来事などのトピックスになっています。

Calendar

<<  March 2025  >>
MoTuWeThFrSaSu
242526272812
3456789
10111213141516
17181920212223
24252627282930
31123456

View posts in large calendar