Подтвердить что ты не робот

Как читать файл csv по одной строке за раз и заменять/редактировать определенные строки по ходу?

У меня есть файл csv объемом 60 ГБ, в котором я должен внести некоторые изменения. Клиент хочет внести некоторые изменения в данные файлов, но я не хочу регенерировать данные в этом файле, потому что это заняло 4 дня.

Как я могу прочитать файл по очереди (не загружая все это в память!) и вносить изменения в эти строки, когда я иду, заменяя определенные значения и т.д.

4b9b3361

Ответ 1

Процесс будет примерно таким:

  • Откройте StreamWriter во временный файл.
  • Откройте StreamReader в целевом файле.
  • Для каждой строки:
    • Разделите текст на столбцы на основе разделителя.
    • Проверьте столбцы на значения, которые вы хотите заменить, и замените их.
    • Объедините значения столбца вместе с помощью разделителя.
    • Введите строку во временный файл.
  • Когда вы закончите, удалите целевой файл и переместите временный файл в путь к целевому файлу.

Примечание относительно Шагов 2 и 3.1: Если вы уверены в структуре своего файла, и это достаточно просто, вы можете сделать все это из коробки, как описано (я включу образец в одно мгновение). Однако в файле CSV есть факторы, которые могут потребовать внимания (например, распознавание, когда разделитель используется буквально в значении столбца). Вы можете выполнить это самостоятельно или попробовать существующее решение.


В основном примере используйте StreamReader и StreamWriter:

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);

Ответ 2

Файлы с отображением памяти - это новая функция в .NET Framework 4, которая может использоваться для редактирования больших файлов. читайте здесь http://msdn.microsoft.com/en-us/library/dd997372.aspx или google Файлы с отображением памяти

Ответ 3

Просто прочитайте файл, построчно, с помощью streamreader, а затем используйте REGEX! Самый удивительный инструмент в мире.

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();
            }

        }