使用.NET如何将包含Latin-1重音字符的ISO 8859-1编码文本文件转换为UTF-8?

25

我收到的文本文件是以ISO 88591-1格式保存的,其中包含来自Latin-1范围内的重音字符(以及普通的ASCII a-z等)。我该如何使用C#将这些文件转换为UTF-8,使ISO 8859-1中的单字节重音字符成为有效的UTF-8字符?

我尝试使用带有ASCIIEncoding的StreamReader,然后通过实例化编码ascii和编码utf8,并使用Encoding.Convert(ascii, utf8, ascii.GetBytes( asciiString) )将ASCII字符串转换为UTF-8 — 但重音字符被渲染为问号。

我错过了什么步骤吗?


你尝试过使用UTF8编码的StreamWriter将asciiString写入文本文件吗?这样做可以吗? - Task
@任务:他的问题在于他从8859-1中无法获取字符串,而不是无法将其保存为UTF-8。 - Adam Robinson
哦,那完全是他的问题,毫无疑问。我只是觉得使用StreamReader/StreamWriter配对来调试文本转换更容易(这样我就可以看到输入/输出文件),而不是使用Encoding.Convert调用。也许这只是我的个人偏好。 - Task
@任务:我同意(因此我的答案!) ;) - Adam Robinson
2个回答

44

你需要获取正确的编码对象。ASCII正如其名称所示,意味着它只支持7位ASCII字符。如果你想要做的是转换文件,则这可能比直接处理字节数组更容易。

using (System.IO.StreamReader reader = new System.IO.StreamReader(fileName,
                                       Encoding.GetEncoding("iso-8859-1")))
{
    using (System.IO.StreamWriter writer = new System.IO.StreamWriter(
                                           outFileName, Encoding.UTF8))
    {
        writer.Write(reader.ReadToEnd());
    }
}

然而,如果你想要字节数组本身,可以使用Encoding.Convert轻松实现。

byte[] converted = Encoding.Convert(Encoding.GetEncoding("iso-8859-1"), 
    Encoding.UTF8, data);

需要注意的是,如果你想采用这种方法,那么不应该使用基于编码的字符串读取器,例如StreamReader进行文件IO操作。更适合的是使用FileStream,因为它将读取文件的实际字节。

为了完全探讨这个问题,可以尝试以下代码:

using (System.IO.FileStream input = new System.IO.FileStream(fileName,
                                    System.IO.FileMode.Open, 
                                    System.IO.FileAccess.Read))
{
    byte[] buffer = new byte[input.Length];

    int readLength = 0;

    while (readLength < buffer.Length) 
        readLength += input.Read(buffer, readLength, buffer.Length - readLength);

    byte[] converted = Encoding.Convert(Encoding.GetEncoding("iso-8859-1"), 
                       Encoding.UTF8, buffer);

    using (System.IO.FileStream output = new System.IO.FileStream(outFileName,
                                         System.IO.FileMode.Create, 
                                         System.IO.FileAccess.Write))
    {
        output.Write(converted, 0, converted.Length);
    }
}
在这个例子中,变量buffer会被实际文件数据以byte[]的形式填充,因此不需要进行任何转换。Encoding.Convert指定源编码和目标编码,然后将转换后的字节存储在名为converted的变量中。然后,它直接写入输出文件中。
正如我所说的,如果这就是你要做的全部内容,使用StreamReaderStreamWriter的第一种选项会更简单,但后一个示例应该更能让你了解实际上正在发生什么。

感谢所有人的帮助,特别是@Adam提供了详尽的答案。 - Tim

17
如果文件比较小(比如约10兆字节),你只需要两行代码:
  string txt = System.IO.File.ReadAllText(inpPath, Encoding.GetEncoding("iso-8859-1"));
  System.IO.File.WriteAllText(outPath, txt);

1
为什么只有在读取的文件小于10兆字节时,您的解决方案才能正常工作? - bit
@bit ReadAllText使用一个默认缓冲区大小为1024的StreamReader,对于较大的文件,您可能需要进行调整。https://web.archive.org/web/20230801072915/https://github.com/microsoft/referencesource/blob/master/mscorlib/system/io/streamreader.cs - fuchs777

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接