potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C# 文字列のANSI文字列→Unicode文字列変換時の注意

概要

ANSI文字列(ここでは純粋なASCIIエンコーディングの文字列ではなくシステム上でANSI版として扱われる文字列。日本語ではShift-JISエンコーディングの文字列)を格納したバイト配列byte[]をstring型へ変換する場合等、ANSI文字列を扱う場合はエンコーディング時のコードページに注意が必要となります。具体的にはANSI文字列をC#のstring型(Unicode)に変換する場合は次の処理が必要となります。

using System.Text;

var buffer = ...
var ansiCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
var s = Encoding.GetEncoding(ansiCodePage).GetString(buffer);

なお、P/Invokeではマーシャリングにより自動変換されるので気にする必要はありません。

詳細

ANSIコードページはシステムの言語設定により異なり、Windowsの日本語環境では基本的にShift-JISです。この設定はSystem.Text.CultureInfo.CurrentCulture.TextInfo.ANSICodePageにより取得することができます。

System.Text.EncodingにはASCIIEncodingが存在します。これはANSIエンコーディングの元となる純粋なASCIIエンコーディングであり、先頭の文字はAですがシステム上のANSIエンコーディングとは異なります。ひらがなや漢字等はACSIIエンコーディングに含まれないため、意図的にANSIエンコーディングを指定しないと文字化けが生じます。

また、日本語環境を決め打ちすればGetEncoding("Shift-JIS")やGetEncoding("SJIS")等を使用することもできますが、System.Text.CultureInfo.CurrentCulture.TextInfo.ANSICodePageを使用すれば各言語環境に対応することができます。

サンプルコード

次のサンプルコードはASCIIエンコーディングとShift-JISエンコーディングによる日本語の扱いの違いを示します。

using System;
using System.Globalization;
using System.Runtime.InteropServices;
using System.Text;

class Program
{
    private static class NativeMethods
    {
        [DllImport("kernel32.dll", CharSet = CharSet.Ansi, ExactSpelling = true)]
        public static extern void lstrcpynA(
            [Out] byte[] lpString1,
            [In] string lpString2,
            int iMaxLength);
    }

    static void Main(string[] args)
    {
        var buffer = new byte[256];
        NativeMethods.lstrcpynA(buffer, "自分用", buffer.Length);

        // ASCIIエンコーディングとしてUnicodeへ変換
        Console.WriteLine(Encoding.ASCII.GetString(buffer)); // "?????p"

        // Shift-JISエンコーディングと決め打ちしてとしてUnicodeへ変換
        Console.WriteLine(Encoding.GetEncoding("SJIS").GetString(buffer)); // "自分用"

        // 現在の言語設定のANSIコードページを利用してUnicodeへ変換
        var ansiCodePage = CultureInfo.CurrentCulture.TextInfo.ANSICodePage;
        Console.WriteLine(Encoding.GetEncoding(ansiCodePage).GetString(buffer)); // "自分用"
    }
}

注意する場合の例

  • PEファイルからCHAR型(C/C++)を読み込む場合

2021/3/10:この記事は別のブログで投稿した記事を移動したものです。