2012-12-21 26 views
11

Tôi có một tệp csv 60 GB tôi cần thực hiện một số sửa đổi. Khách hàng muốn một số thay đổi đối với dữ liệu tệp, nhưng tôi không muốn tạo lại dữ liệu trong tệp đó vì mất 4 ngày để thực hiện.Làm thế nào để đọc một tập tin csv một dòng tại một thời điểm và thay thế/chỉnh sửa các dòng nhất định khi bạn đi?

Làm cách nào để đọc tệp, xếp hàng từng dòng (không tải tất cả vào bộ nhớ!) Và thực hiện chỉnh sửa cho các dòng đó khi tôi đi, thay thế các giá trị nhất định, v.v ...?

+1

trong trường hợp như vậy tại sao bạn không thử sử dụng Hadoop MapReduce .... –

+0

Bạn sẽ có thể làm thay đổi chỉ khi dòng sửa đổi sẽ có chiều dài mà không vượt gốc dòng dài –

+0

Tại sao không chỉ viết thư cho một tập tin mới? Vì vậy: 1. Đọc 2. Sửa đổi 3.Write để sao chép. Đó có phải là điều bạn không muốn làm hay chỉ là tìm kiếm một cách "thanh lịch" để làm điều này? – StampedeXV

Trả lời

13

Quá trình này sẽ là một cái gì đó như thế này:

  1. Mở StreamWriter vào một tập tin tạm thời.
  2. Mở một StreamReader vào tệp đích.
  3. Đối với mỗi dòng:
    1. Tách văn bản thành các cột dựa trên dấu tách.
    2. Kiểm tra các cột cho các giá trị bạn muốn thay thế và thay thế chúng.
    3. Tham gia các giá trị cột lại với nhau bằng dấu phân cách của bạn.
    4. Viết dòng vào tệp tạm thời.
  4. Khi bạn hoàn tất, xóa tệp đích và di chuyển tệp tạm thời sang đường dẫn tệp đích.

Lưu ý về bước 2 và 3.1: Nếu bạn tin tưởng vào cấu trúc tệp của mình và đơn giản là đủ, bạn có thể thực hiện tất cả điều này như được mô tả (Tôi sẽ đưa mẫu vào một lúc). Tuy nhiên, có các yếu tố trong tệp CSV có thể cần chú ý (chẳng hạn như nhận ra khi dấu phân cách đang được sử dụng theo nghĩa đen trong giá trị cột). Bạn có thể tự mình thực hiện điều này hoặc thử một số existing solution.


Basic dụ chỉ sử dụng StreamReaderStreamWriter:

var sourcePath = @"C:\data.csv"; 
var delimiter = ","; 
var firstLineContainsHeaders = true; 
var tempPath = Path.GetTempFileName(); 
var lineNumber = 0; 

var splitExpression = new Regex(@"(" + delimiter + @")(?=(?:[^""]|""[^""]*"")*$)"); 

using (var writer = new StreamWriter(tempPath)) 
using (var reader = new StreamReader(sourcePath)) 
{ 
    string line = null; 
    string[] headers = null; 
    if (firstLineContainsHeaders) 
    { 
     line = reader.ReadLine(); 
     lineNumber++; 

     if (string.IsNullOrEmpty(line)) return; // file is empty; 

     headers = splitExpression.Split(line).Where(s => s != delimiter).ToArray(); 

     writer.WriteLine(line); // write the original header to the temp file. 
    } 

    while ((line = reader.ReadLine()) != null) 
    { 
     lineNumber++; 

     var columns = splitExpression.Split(line).Where(s => s != delimiter).ToArray(); 

     // if there are no headers, do a simple sanity check to make sure you always have the same number of columns in a line 
     if (headers == null) headers = new string[columns.Length]; 

     if (columns.Length != headers.Length) throw new InvalidOperationException(string.Format("Line {0} is missing one or more columns.", lineNumber)); 

     // TODO: search and replace in columns 
     // example: replace 'v' in the first column with '\/': if (columns[0].Contains("v")) columns[0] = columns[0].Replace("v", @"\/"); 

     writer.WriteLine(string.Join(delimiter, columns)); 
    } 

} 

File.Delete(sourcePath); 
File.Move(tempPath, sourcePath); 
+0

Đó chắc chắn là cách đi thẳng đơn giản và thẳng thắn nhất. – richard

+0

Tôi đã cập nhật nó để xử lý các lần xuất hiện chữ của dấu phân tách. – HackedByChinese

+0

Một điều, tôi không nghĩ về kích thước.'File.Move' cuối cùng có thể sẽ rất chậm. Vì vậy, thay vào đó, bạn chỉ có thể tạo tệp tạm thời trong cùng thư mục với tệp nguồn, sau đó xóa nguồn và chỉ đổi tên temp (thay vì sử dụng 'GetTempFileName' và' File.Move'). – HackedByChinese

6

file bộ nhớ ánh xạ là một tính năng mới trong .NET Framework 4 có thể được sử dụng để chỉnh sửa tập tin lớn. đọc tại đây http://msdn.microsoft.com/en-us/library/dd997372.aspx hoặc google Tệp được ánh xạ trên bộ nhớ

+0

Tôi thích nó! Tôi sẽ kiểm tra nó. Cảm ơn. – richard

1

Chỉ cần đọc tệp, từng dòng, với trình tạo luồng và sau đó sử dụng REGEX! Công cụ tuyệt vời nhất trên thế giới.

using (var sr = new StreamReader(new FileStream(@"C:\temp\file.csv", FileMode.Open))) 
     { 
      var line = sr.ReadLine(); 
      while (!sr.EndOfStream) 
      { 
       // do stuff 

       line = sr.ReadLine(); 
      } 

     } 
Các vấn đề liên quan