如何将大文件(> 1 GB)的编码转换为Windows 1252而不会出现内存不足异常?

时间:2017-03-02 09:14:39

标签: c# encoding filestream

考虑:

public static void ConvertFileToUnicode1252(string filePath, Encoding srcEncoding)
{
    try
    {
        StreamReader fileStream = new StreamReader(filePath);
        Encoding targetEncoding = Encoding.GetEncoding(1252);

        string fileContent = fileStream.ReadToEnd();
        fileStream.Close();

        // Saving file as ANSI 1252
        Byte[] srcBytes = srcEncoding.GetBytes(fileContent);
        Byte[] ansiBytes = Encoding.Convert(srcEncoding, targetEncoding, srcBytes);
        string ansiContent = targetEncoding.GetString(ansiBytes);

        // Now writes contents to file again
        StreamWriter ansiWriter = new StreamWriter(filePath, false);
        ansiWriter.Write(ansiContent);
        ansiWriter.Close();
        //TODO -- log success  details
    }
    catch (Exception e)
    {
        throw e;
        // TODO -- log failure details
    }
}

上面的代码会返回大型文件的内存不足异常,仅适用于小型文件。

3 个答案:

答案 0 :(得分:12)

我认为仍然使用StreamReaderStreamWriter,但是阅读字符块而不是一次性或逐行删除是最优雅的解决方案。它不会随意假设文件由可管理长度的行组成,并且它也不会破坏多字节字符编码。

public static void ConvertFileEncoding(string srcFile, Encoding srcEncoding, string destFile, Encoding destEncoding)
{
    using (var reader = new StreamReader(srcFile, srcEncoding))
    using (var writer = new StreamWriter(destFile, false, destEncoding))
    {
        char[] buf = new char[4096];
        while (true)
        {
            int count = reader.Read(buf, 0, buf.Length);
            if (count == 0)
                break;

            writer.Write(buf, 0, count);
        }
    }
}

(我希望StreamReader有一个像CopyTo那样的Stream方法,如果有的话,这实际上就是一个单行!)

答案 1 :(得分:1)

不要读取并一次读取并逐行读取它。如果您阅读结束,则立即将整个文件放入缓冲区。

答案 2 :(得分:-1)

试试这个:

using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
{
    int size = 4096;
    Encoding targetEncoding = Encoding.GetEncoding(1252);
    byte[] byteData = new byte[size];

    using (FileStream outputStream = new FileStream(outputFilepath, FileMode.Create))
    {
        int byteCounter = 0;

        do
        {
            byteCounter = fileStream.Read(byteData, 0, size);

            // Convert the 4k buffer
            byteData = Encoding.Convert(srcEncoding, targetEncoding, byteData);

            if (byteCounter > 0)
            {
                outputStream.Write(byteData, 0, byteCounter);
            }
        }
        while (byteCounter > 0);

        inputStream.Close();
    }
}

可能有一些语法错误,因为我是从内存中完成的,但这就是我如何使用大文件,一次读取一块,进行一些处理并保存块。这是实现它(流式传输)的唯一方式,而不依赖于读取所有内容的大量IO开销和存储所有内存的大量RAM消耗,将其全部转换为内存然后将其全部保存回来。

您可以随时调整缓冲区大小。

如果您希望旧方法在不抛出OutOfMemoryException的情况下工作,则需要告诉垃圾收集器允许非常大的对象。

在App.config中,在<runtime>下添加以下行(您的代码不需要它,但值得了解):

<gcAllowVeryLargeObjects enabled="true" />