Разбор текстового файла данных с Linq - PullRequest
0 голосов
/ 03 сентября 2010

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

....

30AA ALUMINIUM ALLOY     LMELMEUSD2.00  0.35         5101020100818
40AADFALUMINIUM ALLOY USD USD 100   1       0.20000    1.00   0 100  140003
50201008180.999993  0.00  0.00  120100818
60       0F     1  222329 1.000000      0      0  -4667  -4667   4667   4667
50201008190.999986  0.00  0.00  120100819
60       0F     1  222300 1.000000      0      0  -4667  -4667   4667   4667
40AADOALUMINIUM ALLOY USD USD 100   1       0.20000    1.00   0 100  140001
50201009150.000000  0.17  0.17  120100915
60    1200C     1  101779 0.999800      0      0  -4666  -4666   4665   4665
60    1200P     1       0 0.000000      0      0      0      0      0      0
60    1225C     1   99279 0.999800     -1     -1  -4667  -4667   4665   4665
60    1225P     1       0 0.000000      0      0      0      0      0      0
60    1250C     1   96780 0.999800      0      0  -4666  -4666   4665   4665
60    1250P     1       0 0.000000      0      0      0      0      0      0
60    1275C     1   94280 0.999800     -1     -1  -4667  -4667   4665   4665
60    1275P     1       0 0.000000      0      0      0      0      0      0
60    1300C     1   91781 0.999800      0      0  -4666  -4666   4665   4665
60    1300P     1       0 0.000000

.......

Файл содержит иерархические отношения, основанные на двухзначных префиксах. Вы можете думать о «30» строках, содержащих «40», как о детях; «40» строк, содержащих «50», и «50», содержащих «60». После анализа эти строки и связанные с ними префиксы будут, очевидно, отображаться в тип clr, отображение «30» на «ContractGroup», отображение «40» на «InstrumentTypeGroup», «50» на «ExpirationGroup» и т. Д.

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

 public static IEnumerable<string> TextFileLineEnumerator()
 {
     using (StreamReader sr = new StreamReader("BigDataFile.txt"))
     {
         while (!sr.EndOfStream)
         {
             yield return sr.ReadLine();
         }
     }
 }

Это позволяет мне использовать Linq против текстового файла и обрабатывать строки в виде потока.

Моя проблема заключается в попытке обработать этот поток в его композиционную структуру коллекции, вот первая попытка:

  var contractgroups =   from strings in TextFileLineEnumerator()
                          .SkipWhile(s => s.Substring(0, 2) != "30")
                            .Skip(1) where strings.Substring(0,2) != "30"
                              select strings;

Это дает мне все дочерние строки «30» (но, к сожалению, пропускает саму строку «30».) Этот запрос, очевидно, потребует подзапросов для сбора и проецирования строк (с помощью выбора) в их соответствующие типы с соответствующими композициями. (ContractGroups, содержащие список групп InstrumentType и т. Д.)

Эта проблема, скорее всего, сводится к тому, что у меня нет опыта работы с функциональным программированием, поэтому, если у кого-то есть какие-либо указатели на этот вид синтаксического анализа, это было бы полезно, спасибо-

1 Ответ

2 голосов
/ 03 сентября 2010

Мне не совсем ясно точно , что вы пытаетесь сделать, но как бы я подошел к этой проблеме, можно было бы сначала написать функцию PartitionLines, например:

public static IEnumerable<IEnumerable<string>> PartitionLines(
    this IEnumerable<string> source,
    Func<string, string> groupMarkerSelector,
    string delimeter)
{
    List<string> currentGroup = new List<string>();

    foreach (string line in source)
    {
        var key = groupMarkerSelector(line);
        if (delimeter == key && currentGroup.Count > 0)
        {
            yield return currentGroup;
            currentGroup = new List<string>();
        }

        currentGroup.Add(line);
    }

    if (currentGroup.Count > 0)
        yield return currentGroup;
}

(Обратите внимание, что моя функция одновременно загружает в группу «группу»; я предполагаю, что это нормально.)

Я бы тогда взял что-то вроде этого:

var line30Groups =
    TextFileLineEnumerator().
    PartitionLines(l => l.Substring(0, 2), "30");

Теперь у вас есть строки в группах, с новой группой строк, начинающихся каждый раз, когда вы видите «30». Вы можете подразделить дальше:

var line3040Groups =
    TextFileLineEnumerator().
    PartitionLines(l => l.Substring(0, 2), "30").Select(g =>
        g.PartitionLines(l => l.Substring(0, 2), "40"));

Теперь у вас есть строки в группах под «30», и каждая группа представляет собой множество групп под каждым дочерним «40». И так далее.

Это не проверено и может быть чище, но я надеюсь, вы понимаете.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...