(轉載)dotnet core 中文亂碼 codepages

來源:互聯網
上載者:User

標籤:url   引入   pos   並且   iter   代碼   c89   for   風格   

引子

 

轉載自:http://www.jianshu.com/p/1c9c59c5749a

參考:.Net Core 控制台輸出中文亂碼

 

上文中我查閱了一些cli的源碼, 閑來無事就繼續翻代碼, 冥冥之中自有天意, 在無盡的代碼中, 我看到了這樣一個注釋

// by default, .NET Core doesn‘t have all code pages needed for Console apps.// see the .NET Core Notes in https://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx

dotnet core 團隊的思路是好的, 提供一個比較小的程式集, 碼農們按自己的需求自行添加需要的依賴, 可以產生更小的程式集, 完全同意. But, 我猜肯定有人墜坑過.

有人墜麼?

隨便google了一下得出以下結果

果不出我所料, 中獎的兄弟還不少, 不過也早有大神對此事進行過敘述, 並且早已在其文中給出解決方案, 大神的文章在此 難道.NET Core到R2連中文編碼都不支援嗎?(http://www.cnblogs.com/artech/archive/2016/05/18/5507092.html)
有人會問了, 那你還寫個屁文幹啥?

事還沒完!

為啥沒完? 因為看到大神文章的評論中一個兄弟在坑中還沒爬上來. 川酷不能見死不救是不是?
據那位仁兄所說, 事情的經過是這樣的.

        Console.WriteLine("你好, 世界!");        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);        Console.WriteLine("你好, 世界!");

按大神文章所述, 註冊了CodePages 編碼則為UTF-8, 中文不應該亂碼, 可事實是

難道大神也錯了嗎? 當然不會!

        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

正常情況下這句代碼足矣解決問題, 依cli注釋所說, 對於Console程式, 並沒有添加所有的code page, 而且注釋中給出的微軟官方解決方案也是添加codepage 的dll程式集, 並添加上面那句代碼.
這個時候我又找到了另外一個大神的blog,裡面有另一種解決方案

    public static void Main(string[] args)    {        Console.OutputEncoding = System.Text.Encoding.UTF8;//第一種方式:指定編碼        //Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);//第二種方式        Console.Read();    }

既然有方案一, 川酷肯定要試一下.

        Console.WriteLine("你好, 世界!");        Console.OutputEncoding=Encoding.UTF8;        //Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);        Console.WriteLine("你好, 世界!");

大神就是大神, 這招靈!

不過第二行的輸出有一點小問題, 有興趣的童鞋研究一下, 在此不再贅述.

讓我帶你飛!

依川酷的風格, 翻代碼的時候到了! 既然問題出在Console中, 那就找Console的代碼來看下. 有興趣的可以在這裡擷取代碼 . 讓我們找到Console.cs這個檔案, 可以看到相應的WriteLine方法的重載.

    [MethodImplAttribute(MethodImplOptions.NoInlining)]    public static void WriteLine(String value)    {        Out.WriteLine(value);    }

再看Out是個什麼鬼.

    public static TextWriter Out    {        get { return Volatile.Read(ref s_out) ?? EnsureInitialized(ref s_out, () => CreateOutputWriter(OpenStandardOutput())); }    }

然後我們發現s_out就是用於輸出的TextWriter.
並且在第一次調用的時候會建立一個新的.

    private static TextWriter CreateOutputWriter(Stream outputStream)    {        return SyncTextWriter.GetSynchronizedTextWriter(outputStream == Stream.Null ?            StreamWriter.Null :            new StreamWriter(                stream: outputStream,                encoding: new ConsoleEncoding(OutputEncoding), // This ensures no prefix is written to the stream.                bufferSize: DefaultConsoleBufferSize,                leaveOpen: true) { AutoFlush = true });    }

這樣看來TextWriter的Encoding是使用的OutputEncoding, 那我們實踐下, 這個初始的OutputEncoding到底是什麼.

既然是UTF-8為何還是亂碼? 此事還應該從微軟的那句注釋說起, 人家說了, 預設是沒有添加codepage的, 即使你現在是UTF-8編碼, 但是程式中沒有codepage, 當然沒辦法處理. 我們來看看OutputEncoding的源碼吧.

    public static Encoding OutputEncoding    {        get        {            return Volatile.Read(ref s_outputEncoding) ?? EnsureInitialized(ref s_outputEncoding, () => ConsolePal.OutputEncoding);        }        set        {            CheckNonNull(value, "value");            lock (InternalSyncObject)            {                // Set the terminal console encoding.                ConsolePal.SetConsoleOutputEncoding(value);                // Before changing the code page we need to flush the data                 // if Out hasn‘t been redirected. Also, have the next call to                  // s_out reinitialize the console code page.                if (Volatile.Read(ref s_out) != null && !s_isOutTextWriterRedirected)                {                    s_out.Flush();                    Volatile.Write(ref s_out, null);                }                if (Volatile.Read(ref s_error) != null && !s_isErrorTextWriterRedirected)                {                    s_error.Flush();                    Volatile.Write(ref s_error, null);                }                Volatile.Write(ref s_outputEncoding, (Encoding)value.Clone());            }        }    }

我猜問題就出在了ConsolePal.SetConsoleOutputEncoding(value) 這一句.
我相信大家記得, 在我顯性set了OutputEncoding為UTF-8之後控制台的中文顯示就正常了. 也就是說對OutputEncoding做了set動作之後, 會強制Console視窗引入codepage檔案.

Summary

比較一下兩種解決方式, 其實兩者有本質的不同, SetConsoleOutputEncoding是為這個控制台執行個體做編碼的設定, 而 RegisterProvider 是為當前這個程式集添加codepage.
之所以 Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)不起作用是因為前面的一句Console.WriteLine("你好, 世界!")已經使該命令列視窗初始化了編碼. 執行Encoding.RegisterProvider(CodePagesEncodingProvider.Instance)並不對命令列視窗起作用, 在這裡給做一個實驗, 我相信大家就可以清楚中間到底發生了什麼.
首先, 重複剛開始的代碼.

        Console.WriteLine("你好, 世界!");        // Console.OutputEncoding=Encoding.UTF8;        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);        Console.WriteLine("你好, 世界!");

得到的結果是這樣的.

然後為該命令列執行這句Console.OutputEncoding=Encoding.UTF8;. 再看下結果.


其實此時的RegisterProvider方法已經沒有意義了, 因為命令列已經載入了codepage. 就是這樣, 完結!

歡迎大家討論, 如果有覺得不對或不妥的地方也希望大家可以指正, 我會努力寫一些高品質的文章.

(轉載)dotnet core 中文亂碼 codepages

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.