Как преобразовать кодировку большого файла (> 1 ГБ) в размере - в Windows 1252 без исключения из памяти?
Рассмотрим:
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
}
}
Вышеупомянутый фрагмент кода возвращает исключение из памяти для больших файлов и работает только для файлов небольшого размера.
Ответы
Ответ 1
Я все еще использую 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
имел метод CopyTo
, такой как Stream
, если бы он был, это было бы по существу однострочным!)
Ответ 2
Не читатьToEnd и читать его, как строка за строкой или X символов за раз. Если вы читаете до конца, вы сразу же помещаете весь свой файл в буфер.
Ответ 3
Попробуйте следующее:
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();
}
}
Возможно, есть некоторые синтаксические ошибки, как я сделал это из памяти, но так я работаю с большими файлами, читаю в куске за раз, делаю некоторую обработку и сохраняю кусок назад. Это действительно единственный способ сделать это (потоковая передача), не полагаясь на массивные издержки ввода-вывода на чтение всего и огромное количество оперативной памяти, хранящее все это, преобразовывая все это в память и затем сохраняя все это обратно.
Вы всегда можете настроить размер буфера.
Если вы хотите, чтобы ваш старый метод работал, не бросая OutOfMemoryException
, вам нужно сообщить сборщику мусора, чтобы разрешить очень большие объекты.
В App.config, под <runtime>
добавьте следующую строку (вам не нужно это с моим кодом, но это стоит знать):
<gcAllowVeryLargeObjects enabled="true" />