如何将大文件 (>1 GB) 的编码转换为 Windows 1252 而不会出现内存不足异常?
How do I convert encoding of a large file (>1 GB) in size - to Windows 1252 without an out-of-memory exception?
考虑:
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
}
}
以上代码returns大文件内存不足异常,只对小文件有效
不要 readToEnd 而是逐行阅读或一次阅读 X 个字符。如果读到结尾,您会立即将整个文件放入缓冲区。
试试这个:
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" />
我认为仍然使用 StreamReader
和 StreamWriter
但最优雅的解决方案是读取字符块而不是一次或逐行读取所有字符。它不会武断地假设文件由可管理长度的行组成,也不会破坏多字节字符编码。
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
有一个像 Stream
那样的 CopyTo
方法,如果有,这基本上就是一个单行!)
考虑:
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
}
}
以上代码returns大文件内存不足异常,只对小文件有效
不要 readToEnd 而是逐行阅读或一次阅读 X 个字符。如果读到结尾,您会立即将整个文件放入缓冲区。
试试这个:
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" />
我认为仍然使用 StreamReader
和 StreamWriter
但最优雅的解决方案是读取字符块而不是一次或逐行读取所有字符。它不会武断地假设文件由可管理长度的行组成,也不会破坏多字节字符编码。
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
有一个像 Stream
那样的 CopyTo
方法,如果有,这基本上就是一个单行!)