Лучший метод разбора текстового файла в C #? - PullRequest
9 голосов
/ 18 августа 2008

Я хочу разобрать что-то вроде конфигурационного файла, вот так:

[KEY:Value]     
    [SUBKEY:SubValue]

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

Одно ограничение заключается в том, что он должен работать в среде Linux / Mono (если быть точным, 1.2.6). У меня нет последней версии 2.0 (из Mono), поэтому попробуйте ограничить языковые возможности до C # 2.0 или C # 1.0.

Ответы [ 8 ]

12 голосов
/ 18 августа 2008

Я рассмотрел это, но я не собираюсь использовать XML. Я собираюсь писать это вручную, а редактирование XML вручную делает мой мозг болящим. : ')

Вы смотрели на YAML ?

Вы получаете преимущества XML без всякой боли и страданий. Он широко используется в сообществе ruby ​​для таких вещей, как файлы конфигурации, предварительно подготовленные данные базы данных и т. Д.

вот пример

customer:
  name: Orion
  age: 26
  addresses:
    - type: Work
      number: 12
      street: Bob Street
    - type: Home
      number: 15
      street: Secret Road

Здесь, похоже, есть библиотека C # , которую я лично не использовал, но yaml довольно прост, так что "насколько это может быть сложно?" : -)

Я бы сказал, что предпочтительнее изобретать свой собственный специальный формат (и иметь дело с ошибками синтаксического анализатора)

4 голосов
/ 18 августа 2008

Я смотрел почти на эту проблему на днях: эта статья о токенизации строк - это именно то, что вам нужно. Вы захотите определить свои токены как что-то вроде:

@"(?&ltlevel>\s) | " +
@"(?&ltterm>[^:\s]) | " +
@"(?&ltseparator>:)"

Статья довольно хорошо объясняет это. Оттуда вы просто начинаете есть токены, как считаете нужным.

Подсказка: для LL (1) парсера (читай: легко) токены не могут иметь префикс. Если у вас abc в качестве токена, вы не можете иметь ace в качестве токена

Примечание: в статье отсутствует | символы в его примерах, просто добавьте их.

1 голос
/ 27 августа 2008

Существует другая библиотека YAML для .NET , которая находится в стадии разработки. Прямо сейчас он поддерживает чтение потоков YAML и был протестирован на Windows и Mono. Поддержка записи в настоящее время реализуется.

1 голос
/ 18 августа 2008

Использование библиотеки почти всегда предпочтительнее, чем использование собственной библиотеки. Вот краткий список пунктов «О, мне это никогда не понадобится / я не думал об этом», которые в конечном итоге придут, чтобы укусить вас позже:

  • Побег персонажей. Что если вы хотите: в ключе или] в значении?
  • Выход из побега персонажа.
  • Unicode
  • Сочетание табуляции и пробелов (см. Проблемы с синтаксисом, чувствительным к пробелам в Python)
  • Обработка различных форматов возвращаемых символов
  • Обработка сообщений об ошибках синтаксиса

Как и предполагали другие, YAML выглядит как ваша лучшая ставка.

0 голосов
/ 18 августа 2008

@ Gishu

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

Регулярное выражение было немного быстрее для написания (хотя у меня есть некоторый опыт работы с парсерами), но это без хорошего сообщения об ошибках. Как только вы добавите, это становится немного сложнее и дольше.

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

private static Node ParseNode(TextReader reader)
{
    Node node = new Node();
    int indentation = ParseWhitespace(reader);
    Expect(reader, '[');
    node.Key = ParseTerminatedString(reader, ':');
    node.Value = ParseTerminatedString(reader, ']');
}
0 голосов
/ 18 августа 2008

Независимо от сохраняемого формата использование Regex будет самым быстрым способом анализа. В ruby, вероятно, будет несколько строк кода.

\[KEY:(.*)\] 
\[SUBKEY:(.*)\]

Эти два значения дадут вам значение и SubValue в первой группе. Проверьте MSDN о том, как сопоставить регулярное выражение со строкой.

Это то, что каждый должен иметь в своей кошечке. Дни до Регекса выглядели как Ледниковый период.

0 голосов
/ 18 августа 2008

Вы также можете использовать стек и использовать алгоритм push / pop. Этот соответствует открывающим / закрывающим тегам.

public string check()
    {
        ArrayList tags = getTags();


        int stackSize = tags.Count;

        Stack stack = new Stack(stackSize);

        foreach (string tag in tags)
        {
            if (!tag.Contains('/'))
            {
                stack.push(tag);
            }
            else
            {
                if (!stack.isEmpty())
                {
                    string startTag = stack.pop();
                    startTag = startTag.Substring(1, startTag.Length - 1);
                    string endTag = tag.Substring(2, tag.Length - 2);
                    if (!startTag.Equals(endTag))
                    {
                        return "Fout: geen matchende eindtag";
                    }
                }
                else
                {
                    return "Fout: geen matchende openeningstag";
                }
            }
        }

        if (!stack.isEmpty())
        {
            return "Fout: geen matchende eindtag";
        }            
        return "Xml is valid";
    }

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

0 голосов
/ 18 августа 2008

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

@ Бернард: Это правда, что ручное редактирование XML утомительно, но представляемая вами структура уже выглядит очень похоже на XML.

Тогда да, там есть хороший метод.

...