簡體   English   中英

在 C# 中將 ANSI (Windows 1252) 轉換為 UTF8

[英]Convert ANSI (Windows 1252) to UTF8 in C#

我之前在 Stack Overflow 上以一種迂回的方式問過這個問題,這次想把它做對。 如何將 ANSI(代碼頁 1252)轉換為 UTF-8,同時保留特殊字符? (我知道 UTF-8 支持比 ANSI 更大的字符集,但是如果我可以保留 ANSI 支持的所有 UTF-8 字符並將其余字符替換為?或其他東西,那也沒關系)

為什么我要轉換 ANSI → UTF-8

我基本上正在編寫一個程序,將 vCard 文件 (VCF) 拆分為單個文件,每個文件包含一個聯系人。 我注意到諾基亞和索尼愛立信手機以 UTF-8(無 BOM)格式保存備份 VCF 文件,但 Android 以 ANSI (1252) 格式保存它。 天知道其他手機以什么格式保存它們!

所以我的問題是

  1. vCard 文件的字符編碼沒有行業標准嗎?
  2. 哪個更容易解決我的問題? 將 ANSI 轉換為 UTF8(和/或相反)或嘗試檢測輸入文件具有哪種編碼並通知用戶?

tl;dr需要知道如何將字符編碼從 (ANSI / UTF8) 轉換為 (UTF8 / ANSI),同時保留所有特殊字符。

您不應該從一種編碼轉換為另一種編碼。 您必須使用創建文件時使用的編碼來讀取每個文件,否則您將丟失信息。

使用正確的編碼讀取文件后,您將獲得 Unicode 字符串形式的內容,然后您可以使用您喜歡的任何編碼保存它。

如果需要檢測編碼,可以將文件作為字節讀取,然后查找特定於任一編碼的字符代碼。 如果文件不包含特殊字符,則任一編碼都將起作用,因為這兩種編碼的字符 32..127 相同。

按照第 3.4 章中的規范要求,VCF 以 utf-8 編碼。 你需要認真對待這一點,如果不是一成不變的,這種格式將毫無用處。 如果您看到某些 Android 應用程序對重音字符進行了重整,請假設這是該應用程序中的錯誤。 或者更有可能的是,它從其他地方獲得了錯誤信息。 您嘗試更正編碼會導致更多問題,因為您的卡版本永遠不會與原始卡匹配。

您使用 Encoding.GetEncoding(1252).GetString() 從 1252 轉換為 utf-8,傳入一個byte[] 永遠不要試圖讀取一個字符串,它敲敲罷了到一個字節寫入代碼[]所以你可以使用的轉換方法,只是使編碼問題變得更糟。 換句話說,您需要使用 FileStream 而不是 StreamReader 讀取文件。 但同樣,避免解決其他人的問題。

這是我在 C# 中使用的(我一直在使用它從 Windows-1252 轉換為 UTF8)

    public static String readFileAsUtf8(string fileName)
    {
        Encoding encoding = Encoding.Default;
        String original = String.Empty;

        using (StreamReader sr = new StreamReader(fileName, Encoding.Default))
        {
            original = sr.ReadToEnd();
            encoding = sr.CurrentEncoding;
            sr.Close();
        }

        if (encoding == Encoding.UTF8)
            return original;

        byte[] encBytes = encoding.GetBytes(original);
        byte[] utf8Bytes = Encoding.Convert(encoding, Encoding.UTF8, encBytes);
        return Encoding.UTF8.GetString(utf8Bytes);
    }

我這樣做:

    private static void ConvertAnsiToUTF8(string inputFilePath, string outputFilePath)
    {
        string fileContent = File.ReadAllText(inputFilePath, Encoding.Default);
        File.WriteAllText(outputFilePath, fileContent, Encoding.UTF8);
    }

我在將大量古代文本文件處理成格式良好的 PDF 時發現了這個問題。 所有文件都沒有 BOM,並且最舊的文件包含導致錯誤解碼為 UTF8 的代碼頁 1252 代碼點。 這只發生在某些時候,UTF8 大部分時間都在工作。 此外,最新的文本數據確實包含 UTF8 代碼點,所以它是一個混合包。

因此,我還設置了“檢測輸入文件具有哪種編碼”並閱讀了如何檢測文本文件的字符編碼? 以及如何確定文本的編碼? 得出的結論是,這充其量是困難的。

但是,我在評論中找到了每個軟件開發人員絕對必須了解 Unicode 和字符集的絕對最小值,閱讀它,並找到了這個寶石:

UTF-8 有一個巧妙的副作用,即英語文本在 UTF-8 中看起來與在 ASCII 中完全相同,因此美國人甚至不會注意到任何錯誤。 只有世界其他地方必須跳過箍。 具體來說,你好,即 U+0048 U+0065 U+006C U+006C U+006F,將存儲為 48 65 6C 6C 6F,看哪! 與存儲在 ASCII、ANSI 和地球上的每個 OEM 字符集相同。

整篇文章很短,值得一讀。

所以,我用下面的代碼解決了我的問題。 由於我的文本數據中只有少量包含困難的字符代碼點,因此我不介意異常處理的性能開銷,特別是因為這只需要運行一次。 也許有更聰明的方法可以避免try/catch但我沒有費心設計一個。

    public static string ReadAllTextFromFile(string file)
    {
        const int WindowsCodepage1252 = 1252;

        string text;

        try
        {
            var utf8Encoding = Encoding.GetEncoding("UTF-8", EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback); 
            text = File.ReadAllText(file, utf8Encoding);
        }
        catch (DecoderFallbackException dfe)//then text is not entirely valid UTF8, contains Codepage 1252 characters that can't be correctly decoded to UTF8
        {
            var codepage1252Encoding = Encoding.GetEncoding(WindowsCodepage1252, EncoderFallback.ExceptionFallback, DecoderFallback.ExceptionFallback);
            text = File.ReadAllText(file, codepage1252Encoding);
        }

        return text;
    }

還值得注意的是StreamReader類具有采用特定 Encoding 對象的構造函數,正如我所展示的,您可以調整 EncoderFallback/DecoderFallback 行為以滿足您的需要。 因此,如果您需要 StreamReader 或StreamWriter來進行更細粒度的工作,仍然可以使用這種方法。

我用它來將文件編碼轉換為 UTF-8

public static void ConvertFileEncoding(String sourcePath, String destPath)
        {
            // If the destination's parent doesn't exist, create it.
            String parent = Path.GetDirectoryName(Path.GetFullPath(destPath));
            if (!Directory.Exists(parent))
            {
                Directory.CreateDirectory(parent);
            }

            // Convert the file.
            String tempName = null;
            try
            {
                tempName = Path.GetTempFileName();
                using (StreamReader sr = new StreamReader(sourcePath))
                {
                    using (StreamWriter sw = new StreamWriter(tempName, false, Encoding.UTF8))
                    {
                        int charsRead;
                        char[] buffer = new char[128 * 1024];
                        while ((charsRead = sr.ReadBlock(buffer, 0, buffer.Length)) > 0)
                        {
                            sw.Write(buffer, 0, charsRead);
                        }
                    }
                }
                File.Delete(destPath);
                File.Move(tempName, destPath);
            }
            finally
            {
                File.Delete(tempName);
            }
        }
  1. vCard 文件的字符編碼沒有行業標准嗎?
  2. 哪個更容易解決我的問題? 將 ANSI 轉換為 UTF8(和/或相反)或嘗試檢測輸入文件具有哪種編碼並通知用戶?

我是如何解決這個問題的:我有 vCard 文件 (*.vcf) - 俄語文件中的 200 個聯系人......我用 vCardOrganizer 2.1 程序打開它,然后使用 Split 將它划分為 200 ......我看到的是 -接觸雜亂的符號,只有我能讀懂它的數字:-) ...

步驟:(執行此步驟時請耐心等待,有時需要時間)用“記事本”打開 vCard 文件(我的文件大小為 3mb)然后從菜單 - 文件-另存為..在打開的窗口中選擇文件名,不要忘記把 .vcf 和編碼 - ANSI 或 UTF-8...最后點擊保存...我將 filename.vcf (UTF-8) 轉換為 filename.vcf (ANSI) - 沒有丟失和完美可讀的俄語語言...如果你有任務寫:yoshidakatana@gmail.com

祝你好運 !!!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM