Какой самый быстрый способ анализа текста с помощью пользовательских разделителей и некоторых очень и очень больших значений полей в C #? - PullRequest
3 голосов
/ 04 декабря 2008

Я пытался разобраться с некоторыми текстовыми файлами с разделителями, которые имеют нестандартные разделители (не разделенные запятыми / кавычками или символами табуляции). Разделители - это случайные символы 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...

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

Ответы [ 6 ]

5 голосов
/ 04 декабря 2008

Используйте File Helpers API . Это .NET и с открытым исходным кодом. Это чрезвычайно высокая производительность при использовании скомпилированного кода IL для установки полей в строго типизированных объектах и ​​поддержки потоковой передачи.

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

Если по какой-то причине вам это не подходит, попробуйте читать строку за строкой со строкой. 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();

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

1 голос
/ 30 сентября 2009

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

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

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

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

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

0 голосов
/ 30 сентября 2009

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

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

Что касается больших размеров данных, заставьте ваш парсер работать, читая по одному байту за раз, и используйте простой конечный автомат, чтобы решить, что делать. Оставьте потоковую передачу и буферизацию базовому классу 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();
}
0 голосов
/ 04 декабря 2008

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

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

0 голосов
/ 04 декабря 2008

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

0 голосов
/ 04 декабря 2008

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

...