Как полностью загрузить файл и обработать запись csvreader?

Я использую программу для чтения CSV и обнаружил, что анализ данных занимает много времени. как я могу загрузить весь CSV-файл в память, а затем обработать его запись за записью, поскольку мне нужно выполнить пользовательское сопоставление записей.

  TextReader tr = new StreamReader(File.Open(@"C:\MarketData\" + symbol + ".txt", FileMode.Open));
  CsvReader csvr = new CsvReader(tr);
  while (csvr.Read())
{
// do your magic
}

person junkone    schedule 07.08.2017    source источник
comment
Что ж, потоки .NET уже используют буферизацию, так что вы уверены, что проблема в этом? Насколько велик файл? Вы можете попробовать асинхронные методы   -  person MickyD    schedule 08.08.2017
comment
это около 10 МБ на файл   -  person junkone    schedule 08.08.2017


Ответы (2)


Чтобы ответить на ваш вопрос напрямую: вы можете полностью загрузить файл в поток памяти, а затем повторно прочитать из этого потока с помощью CsvReader. Точно так же вы можете создать больший буфер чтения для вашего файлового потока, например, 15 МБ, который будет считывать весь файл в буфер за одно обращение. Я сомневаюсь, что любой из них действительно улучшит производительность для файлов размером 10 МБ.

Найдите свое реальное узкое место в производительности: время на чтение содержимого файла с диска, время на разбор CSV в поля или время на обработку записи? Файл размером 10 МБ выглядит очень маленьким. Я обрабатываю наборы CSV-файлов размером более 250 МБ с помощью специальной программы чтения CSV без каких-либо жалоб.

Если обработка является узким местом, и у вас есть несколько доступных потоков, а формат вашего CSV-файла не должен поддерживать экранированные разрывы строк, вы можете прочитать весь файл в список строк (System.IO.File.ReadAllLines / .ReadLines) и анализировать каждую строку, используя другую задачу. Например:

System.IO.File.ReadLines()
.Skip(1)                  // header line. Assume trusted to be correct.
.AsParallel()
.Select(ParseRecord)      // RecordClass ParseRecord(string line)
.ForAll(ProcessRecord);   // void ProcessRecord(RecordClass)

Если у вас есть много файлов для анализа, вы можете обрабатывать каждый файл в отдельной задаче и использовать асинхронные методы, чтобы максимизировать пропускную способность. Если все они поступают с одного и того же физического диска, то ваш пробег будет отличаться и может даже ухудшиться, чем однопоточный подход.

Более продвинутый:

Если вы знаете, что ваши файлы содержат только 8-битные символы, вы можете работать с массивами байтов и пропустить накладные расходы StreamReader для преобразования байтов в символы. Таким образом, вы можете прочитать весь файл в массив байтов за один вызов и сканировать разрывы строк, предполагая, что не требуется поддержка экранирования разрывов строк. В этом случае поиск разрывов строк может выполняться несколькими потоками, каждый из которых просматривает часть массива байтов.

Если вам не нужно поддерживать экранирование полей (a, "b, c", d), вы можете написать более быстрый синтаксический анализатор, просто ищущий разделители полей (обычно запятую). Вы также можете разделить синтаксический анализ разграничения полей и синтаксический анализ содержимого поля на потоки, если это является узким местом, хотя локальность доступа к памяти может свести на нет все преимущества.

При определенных обстоятельствах вам может не понадобиться анализировать поля в промежуточные структуры данных (например, двойные числа, строки) и вы можете обрабатывать непосредственно ссылки на начало/конец полей и избавить себя от создания промежуточной структуры данных.

person typpo    schedule 08.08.2017

Создайте класс, который точно представляет/отражает ваш файл CSV. Затем прочитайте все содержимое в список этого класса. Следующий фрагмент взят из документации CsvHelper.

var csv = new CsvReader( textReader );
var records = csv.GetRecords<MyClass>().ToList();

Важной частью является .ToList(), так как это приведет к загрузке всех данных в ваш список, а не к получению результатов при доступе к ним.

Затем вы можете выполнить дополнительное сопоставление / извлечение в этом списке, который будет в памяти.

Если вы уже делаете это, вам может быть полезно загрузить свой csv в HashSet, а не в список через (ToHashSet()). См. раздел HashSet и производительность списка.

person gcaton    schedule 08.08.2017