在本篇博客中,我將介紹如何在WPF應用程式中使用OwinSelfHost和Swagger來創建自托管的Web API。我們將使用WPF作為我們的應用程式界面,OwinSelfHost來自托管我們的Web API,並使用Swagger來為我們的API生成文檔。 首先,確保你的電腦上已安裝了以下組件: ...
目錄
問題緣由
由於公司需求,需要讀取游戲Redis數據做內外網數據遷移,沒有與游戲組過多的溝通。 使用的數據類型是Hash, key是string,value是byte[]。以前對於編碼的理解是:電腦底層存儲的永遠是01的二進位數據,編碼是一種對於電腦二進位數據的字元映射,也就是約定好哪個值對應哪個字元。是為了便於在顯示器上展示。
那麼基於這個理解,我就以為 不需要關心存儲的數據類型,因為我不需要用到數據,我只是負責做數據的搬運。於是我用的是HGetAsync方法去讀的字元串。然後HSetAsync把數據存到另一個Redis。結果發現數據發生了編碼。基於我上邊對於編碼的理解,也就是按照不同的編碼讀取字元串,只是顯示器上會亂碼,但是底層的01二進位沒有發生變化,這次問題打破了我的認知。
背後原理
當一個byte[]在電腦中存儲時,它就是以二進位形式保存的。如果這個byte[]中的每一個位元組代表的是ASCII碼(一個位元組表示一個字元),那麼它在不同的編碼下讀取應該沒有問題。但是,如果它代表的是Unicode字元集(UTF-8和UTF-16等),那麼在不同的編碼下讀取就會發生問題。因為不同的編碼方式對存儲方式和位元組長度都有不同的要求。
以UTF-8為例,它對不同字元分配的位數不同。對於ASCII字元,UTF-8使用一個位元組表示,而對於其他字元,它需要兩個位元組、三個位元組或四個位元組來表示。因此,在按照UTF-8格式讀取一個byte[]時,如果它的編碼確實是UTF-8,那麼就可以讀取正確的字元。但是,如果重新以UTF-8的格式存儲它時,就會按照UTF-8的編碼方式重新把這個字元轉換成二進位。如果這個字元之前的編碼不是UTF-8,那麼它在轉換為UTF-8的二進位時,就會變成不同的值,因此數據也就變了。
C#代碼示例
var data = Encoding.UTF32.GetBytes("愛"); var word = Encoding.UTF8.GetString(data); var word1 = Encoding.UTF32.GetString(data); File.WriteAllText($@"{AppDomain.CurrentDomain.BaseDirectory}/code.txt", word); File.WriteAllText($@"{AppDomain.CurrentDomain.BaseDirectory}/code1.txt", word1); foreach (var d in File.ReadAllBytes($@"{AppDomain.CurrentDomain.BaseDirectory}/code.txt")) { Console.WriteLine(d); } Console.WriteLine("------------"); foreach (var d in File.ReadAllBytes($@"{AppDomain.CurrentDomain.BaseDirectory}/code1.txt")) { Console.WriteLine(d); }
例如,我們有一個Unicode字元“愛”,其二進位表示為:0000 0100 1110 0111。按照UTF-8編碼的規則,在存儲這個字元時,我們需要使用3個位元組的二進位數據:1110XXXX 10XXXXXX 10XXXXXX(X表示對應字元的二進位數據的高位)
我們將其存儲到一個byte[]中,再將其存儲到文件中。然後按照UTF-8的格式讀取,解析出Unicode字元“愛”,再將其按照UTF-8的格式存儲迴文件。這時,由於使用了UTF-8編碼,我們需要將Unicode字元“愛”轉換為UTF-8編碼的二進位數據,即,使用3個位元組的二進位數據:11100100 10101110 10011111。
通過運行代碼,可以看到,由於存儲使用了UTF-8編碼,而讀取和重新存儲又使用了UTF-8編碼,因此二進位數據發生了變化。
總結
當一個byte[]在電腦中存儲時,它就是以二進位形式保存的。如果這個byte[]中的每一個位元組代表的是ASCII碼(一個位元組表示一個字元),那麼它在不同的編碼下讀取應該沒有問題。但是,如果它代表的是Unicode字元集(UTF-8和UTF-16等),那麼在不同的編碼下讀取就會發生問題。