none
C#で文字コードの判定・変換をする方法について RRS feed

  • 質問

  • C#の書き方について質問です。

    文字コードShift-JISで書かれたファイルをUTF-8で開いた後に、
    文字化けしてしまった文字列をShift-JISで正しく読み直す方法を探しています。

           public static string ConvertCharacterCode(string message)
           {
               var sjis = Encoding.GetEncoding("Shift_JIS");
               var utf8 = Encoding.UTF8;
               var utf8Byte = utf8.GetBytes(message);
               var sjisByte = Encoding.Convert(utf8, sjis, utf8Byte);
               var sjisStr = sjis.GetString(sjisConvertByte);
               return sjisStr;
           }

    messageに文字化けした文字列が入力されます。

    実際に文字コードShift-JISで「あいうえおかきくけこ」と入力したとき、
    messageの内容は「��������������������」(文字化けした文字列)でした。

    今回、このコードはAzure Strema AnalyticsのUDF C#として作成しており、messageをstringではなく、byte[]やchar[]で受け取ろうとすると以下のエラーとなり、動作しませんでした。

    Error : **System Exception** ASA passed a non-supported type System.Byte[] to be marshaled to CSharp UDF ConvertCharacterCode at variable message
    なにかいい方法はございませんでしょうか。よろしくお願いいたします。

    2019年10月10日 10:25

回答

  • Azure Stream Analyticsは標準だとUTF-8しか受け付けていないです。

    ですが、Azure Stream Analytics でカスタム デシリアライザーを実装する (プレビュー段階)というのがあって、受信した生のバイナリを変換する方法があります。
    VS2019のプロジェクトで"Azure Stream Analytics Custom Deserializer Project(.NET)"を作成します。

    using System.Collections.Generic;
    using System.IO;
    
    using Microsoft.Azure.StreamAnalytics;
    using Microsoft.Azure.StreamAnalytics.Serialization;
    
    namespace ASACustomDeserializerProject
    {
        // Deserializes a stream into objects of type CustomEvent.
        // It reads the Stream line by line and assumes each line has three columns separated by ",".
        // Writes an error to diagnostics and skips the line otherwise.
        public class CustomCsvDeserializer : StreamDeserializer<CustomEvent> //CustomEventの中身をデータに合わせて変更
        {
            // streamingDiagnostics is used to write error to diagnostic logs
            private StreamingDiagnostics streamingDiagnostics;
    
            // Initializes the operator and provides context that is required for publishing diagnostics
            public override void Initialize(StreamingContext streamingContext)
            {
                this.streamingDiagnostics = streamingContext.Diagnostics;
            }
    
            // Deserializes a stream into objects of type CustomEvent
            public override IEnumerable<CustomEvent> Deserialize(Stream stream)
            {
                //ここでEncodingを指定してやる
                using (var sr = new StreamReader(stream, System.Text.Encoding.GetEncoding("sjis")))
                {
                    //以下は自分のファイルの内容に合わせて書き換える

    デシリアライザー側のdllをビルドしたら、ユーザー定義関数(UDF)側で参照して、input.jsonでイベントのシリアル化の形式をCustomClrに設定してやればdllを認識してくれます。
    必要ならScript.asaqlをCustomEventのプロパティ名に合わせて書き換えます。
    これで、UDFでの引数はstring型にできます。

    UTF-8とSJISのファイルが混在しているなら、streamをバイト配列に読み切ってから、UTF-8で開いて文字化け(U+FFFD)したらSJISで開きなおすなどの消極的方法をとるしかないです。

    #プレビューなので実運用は難しいけど


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2019年10月11日 1:12

すべての返信

  • 逆パターン(UTF-8 ファイルを Shift_JIS でデコードした場合)は比較的復元しやすいのですが
    Shift_JIS ファイルを UTF-8 でデコードした時の文字化けを復元するのは、ほぼ無理かと。

    『UTF-8 として読み取ることが可能なバイト列』を誤読したことによる文字化けであれば、復元できる可能性があります。

    しかし、『UTF-8 としてありえないバイト列』が渡された場合は、必ず不可逆的な破損を引き起こします。
    この場合、復元は原理的に不可能です。

    不正なバイト列が渡された場合は以下のいずれかの状態になり、元のデータの情報が既に失われている状態であるためです。

    1. 文字列化失敗。データエラーとして処理される。
    2. 不正データの読み飛ばし。欠損データとして処理される。
    3. 不正データを代替文字で置換。破損による文字化けとして処理される。
      ※代替文字としては「�」U+FFFD "REPLACEMENT CHARACTER" や
       「?」U+003F "QUESTION MARK" などが使われます。

    static void Main()
    {
        /*** サンプルの Shift_JIS ファイル ***/
        File.WriteAllBytes("SJIS.TXT", new byte[] { 0x81, 0xe6, 0xfa, 0x5b, 0x87, 0x9a });
    
        // 正しく Shift_JIS で読み取られた正解データ。
        string correct = File.ReadAllText("SJIS.TXT", Encoding.GetEncoding(932));
    
        // 誤って UTF-8 で読み取った文字化けデータ。この時点で破損しているので手の施しようが無い。
        string garbled = File.ReadAllText("SJIS.TXT", Encoding.UTF8);
    
        // 復元を試みるも、既に手遅れ。
        string proposal = ConvertCharacterCode(garbled);
    
        Console.WriteLine("正解:" + correct);
        Console.WriteLine("破損:" + garbled);
        Console.WriteLine("復元:" + proposal);
    
        Console.ReadKey();
    }
    
    public static string ConvertCharacterCode(string message, bool throwIfBroken = false)
    {
        var enc = throwIfBroken ? EncoderFallback.ExceptionFallback : EncoderFallback.ReplacementFallback;
        var dec = throwIfBroken ? DecoderFallback.ExceptionFallback : DecoderFallback.ReplacementFallback;
    
        var sjis = Encoding.GetEncoding("Shift_JIS", enc, dec);
        var utf8 = Encoding.GetEncoding("UTF-8", enc, dec);
    
        return sjis.GetString(utf8.GetBytes(message));
    }


    2019年10月10日 12:12
  • Azure Stream Analyticsは標準だとUTF-8しか受け付けていないです。

    ですが、Azure Stream Analytics でカスタム デシリアライザーを実装する (プレビュー段階)というのがあって、受信した生のバイナリを変換する方法があります。
    VS2019のプロジェクトで"Azure Stream Analytics Custom Deserializer Project(.NET)"を作成します。

    using System.Collections.Generic;
    using System.IO;
    
    using Microsoft.Azure.StreamAnalytics;
    using Microsoft.Azure.StreamAnalytics.Serialization;
    
    namespace ASACustomDeserializerProject
    {
        // Deserializes a stream into objects of type CustomEvent.
        // It reads the Stream line by line and assumes each line has three columns separated by ",".
        // Writes an error to diagnostics and skips the line otherwise.
        public class CustomCsvDeserializer : StreamDeserializer<CustomEvent> //CustomEventの中身をデータに合わせて変更
        {
            // streamingDiagnostics is used to write error to diagnostic logs
            private StreamingDiagnostics streamingDiagnostics;
    
            // Initializes the operator and provides context that is required for publishing diagnostics
            public override void Initialize(StreamingContext streamingContext)
            {
                this.streamingDiagnostics = streamingContext.Diagnostics;
            }
    
            // Deserializes a stream into objects of type CustomEvent
            public override IEnumerable<CustomEvent> Deserialize(Stream stream)
            {
                //ここでEncodingを指定してやる
                using (var sr = new StreamReader(stream, System.Text.Encoding.GetEncoding("sjis")))
                {
                    //以下は自分のファイルの内容に合わせて書き換える

    デシリアライザー側のdllをビルドしたら、ユーザー定義関数(UDF)側で参照して、input.jsonでイベントのシリアル化の形式をCustomClrに設定してやればdllを認識してくれます。
    必要ならScript.asaqlをCustomEventのプロパティ名に合わせて書き換えます。
    これで、UDFでの引数はstring型にできます。

    UTF-8とSJISのファイルが混在しているなら、streamをバイト配列に読み切ってから、UTF-8で開いて文字化け(U+FFFD)したらSJISで開きなおすなどの消極的方法をとるしかないです。

    #プレビューなので実運用は難しいけど


    個別に明示されていない限りgekkaがフォーラムに投稿したコードにはフォーラム使用条件に基づき「MICROSOFT LIMITED PUBLIC LICENSE」が適用されます。(かなり自由に使ってOK!)

    2019年10月11日 1:12
  • ありがとうございます。カスタムデシリアライザーで上手くできそうです!
    2019年10月11日 9:52