標籤: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