2017-03-02 30 views
14

考慮:如何將大文件(大於1 GB)的編碼轉換爲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 
    } 
} 

上面這段代碼返回內存外的一個異常大文件和僅適用於小尺寸的文件。

+12

你能不能用線做線? – BugFinder

+8

您不需要使用ReadToEnd讀取整個內容。閱讀塊,轉換,寫入,重複。 – Evk

+3

使用'foreach(File.ReadLines(filePath)中的字符串行)...處理行...' –

回答

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沒有,如果有,這將基本上是一個班輪!)

+0

Thanks @Matti。這個問題幫助我完成任務。我可以轉換文件的編碼超過1.5GB沒有任何例外。 –

1

不要readToEnd並一次一行讀取或逐行讀取它。如果你閱讀結束,你將整個文件一次放入緩衝區。

-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讀取所有內容以及巨大的內存消耗將所有內容全部存儲在內存中,然後將其全部存回。

您可以隨時調整緩衝區大小。

如果您希望舊方法在不投擲OutOfMemoryException的情況下工作,則需要告知垃圾收集器允許使用非常大的對象。

在App.config中,<runtime>下添加此以下行(你不應該跟我的代碼需要它,但它是值得了解的):

<gcAllowVeryLargeObjects enabled="true" /> 
+4

這只是不適用於所有輸入。輸入採用UTF8格式,不能保證通過精確讀取4K字節,您不會讀取以多字節編碼的部分字符。如果發生這種情況,它將不會被正確讀取,並且您將擁有無效的數據。 –

+0

在引用UTF8的問題中,我看不到任何地方,是否將Source Encoding作爲參數傳入?是的,它將需要調整爲UTF8,但是,如果你的文件都在一行(爲了節省空間,不使用不必要的空白或新的行,如XML),然後逐行進行將無法工作,只有我知道正在流式傳輸文件。基於正在讀取的部分數據,可以在每次迭代中始終調整緩衝區大小。 –

+0

OP正在使用的['StreamReader(string path)'](https://msdn.microsoft.com/en-us/library/f2ke0fzy(v = vs.110).aspx)構造函數將輸入流打開爲UTF8。請參閱鏈接的文檔。在所有文本都在同一行的極端情況下,正確的方法是使用['StreamReader.Read()'](https://msdn.microsoft.com/en-us/library/9kstw824( v = vs.110).aspx)從文件讀取指定數量字符的重載。切勿讀取固定大小的緩衝區,以便從字符可能具有可變長度編碼的文件中讀取。這幾乎總是一個錯誤。 –