Какой самый быстрый способ разобрать текст с пользовательскими разделителями и некоторыми очень, очень большими значениями полей в C #?

StackOverflow https://stackoverflow.com/questions/339496

Вопрос

Я пытался разобраться с некоторыми текстовыми файлами с разделителями, которые имеют нестандартные разделители (не разделенные запятой / кавычками или табуляцией).Разделители - это случайные символы ASCII, которые не часто появляются между разделителями.После поиска я, похоже, не нашел ни одного решения в .NET, которое соответствовало бы моим потребностям, и пользовательские библиотеки, которые люди написали для этого, похоже, имеют некоторые недостатки, когда дело доходит до гигантского ввода (файл объемом 4 ГБ с некоторыми значениями полей, которые очень легко содержат несколько миллионов символов).

Хотя это кажется немного экстремальным, на самом деле в индустрии обнаружения электронных документов (EDD) стандартом для некоторых программ проверки является наличие значений полей, содержащих полное содержимое документа.Для справки, я ранее делал это на python, используя csv-модуль, без проблем.

Вот пример ввода:

Field delimiter = 
quote character = þ

þFieldName1þþFieldName2þþFieldName3þþFieldName4þ
þValue1þþValue2þþValue3þþSomeVery,Very,Very,Large value(5MB or so)þ
...etc...

Редактировать:Поэтому я пошел дальше и создал анализатор файлов с разделителями с нуля.Я немного устал использовать это решение, так как оно может быть подвержено ошибкам.Также не кажется "элегантным" или правильным писать свой собственный синтаксический анализатор для подобной задачи.У меня также есть ощущение, что мне, вероятно, в любом случае не пришлось писать парсер с нуля для этого.

Это было полезно?

Решение

Используйте Файловые помощники API.Это .СЕТЬ с открытым исходным кодом.Это чрезвычайно высокая производительность, использующая скомпилированный IL-код для установки полей в строго типизированных объектах, и поддерживает потоковую передачу.

Он поддерживает все виды типов файлов и пользовательских разделителей;Я использовал его для чтения файлов размером более 4 ГБ.

Если по какой-то причине у вас это не получается, попробуйте просто читать построчно с помощью string.split:

public IEnumerable<string[]> CreateEnumerable(StreamReader input)
{
    string line;
    while ((line = input.ReadLine()) != null)
    {
        yield return line.Split('þ');
    }
}

Это даст вам простые строковые массивы, представляющие строки в потоковом виде, в которые вы даже можете встроить Linq ;) Помните, однако, что IEnumerable загружается с задержкой, поэтому не закрывайте и не изменяйте StreamReader до тех пор, пока вы не выполните итерацию (или не вызовете операцию полной загрузки, такую как ToList / toArray или тому подобное - однако, учитывая ваш размер файла, я предполагаю, что вы этого не сделаете!).

Вот хороший пример его использования:

using (StreamReader sr = new StreamReader("c:\\test.file"))
{
    var qry = from l in CreateEnumerable(sr).Skip(1)
              where l[3].Contains("something")
              select new { Field1 = l[0], Field2 = l[1] };
    foreach (var item in qry)
    {
        Console.WriteLine(item.Field1 + " , " + item.Field2);
    }
}
Console.ReadLine();

Это позволит пропустить строку заголовка, затем распечатать первые два поля из файла, где 4-е поле содержит строку "что-то".Он сделает это без загрузки всего файла в память.

Другие советы

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

Это при том понимании, что вы хотите использовать C # / .NET, и в соответствии с Джо Даффи

18) Не используйте вызовы асинхронных процедур Windows (APCS) в управляемом коде .

Мне пришлось выучить это на собственном горьком опыте ;), но, исключая использование APC, IOCP - единственный разумный вариант.Он также поддерживает множество других типов ввода-вывода, часто используемых на серверах сокетов.

Что касается синтаксического анализа фактического текста, ознакомьтесь У Эрика Уайта ведите блог для некоторого упрощенного использования потока.

Я был бы склонен использовать комбинацию файлов, отображенных в памяти (msdn указывает на .NET-оболочку здесь) и простой инкрементный синтаксический анализ, возвращающий к списку IEnumerable вашей записи / текстовой строки (или чему-либо еще)

Вы упомянули, что некоторые поля очень-очень большие, и если вы попытаетесь прочитать их полностью по памяти, у вас могут возникнуть проблемы.Я бы прочитал файл размером 8 КБ (или небольшими фрагментами), проанализировал текущий буфер, отслеживал состояние.

Что вы пытаетесь сделать с этими данными, которые вы анализируете?Вы что-то ищете?Вы трансформируете это?

Я не вижу проблемы в том, что вы напишете пользовательский синтаксический анализатор.Требования кажутся достаточно отличными от всего, что уже предусмотрено BCL, так что действуйте прямо сейчас.

"Элегантность", очевидно, вещь субъективная.На мой взгляд, если API вашего анализатора выглядит и работает как стандартный API типа BCL "reader", то это довольно "элегантно".

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

Пример того, как вы могли бы использовать такой класс синтаксического анализатора:

using(var reader = new EddReader(new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.Read, 8192)) {
    // Read a small field
    string smallField = reader.ReadFieldAsText();
    // Read a large field
    Stream largeField = reader.ReadFieldAsStream();
}

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top