Лучший метод разбора текстового файла в C#?
Я хочу разобрать что-то вроде конфигурационного файла, вот так:
[KEY:Value]
[SUBKEY:SubValue]
Теперь я начал с StreamReader
преобразование строк в массивы символов, когда я понял, что должен быть лучший способ. Поэтому я прошу вас, скромный читатель, помочь мне.
Одно ограничение заключается в том, что он должен работать в среде Linux/Mono (если быть точным, 1.2.6). У меня нет последней версии 2.0 (из Mono), поэтому попробуйте ограничить языковые возможности до C# 2.0 или C# 1.0.
8 ответов
Я рассмотрел это, но я не собираюсь использовать 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 довольно прост, так что "как это может быть сложно?":-)
Я бы сказал, что предпочтительнее изобретать свой собственный специальный формат (и иметь дело с ошибками парсера)
Я смотрел почти на эту проблему на днях: эта статья о токенизации строк именно то, что вам нужно. Вы захотите определить свои токены как что-то вроде:
@"(?<level>\s) | " +
@"(?<term>[^:\s]) | " +
@"(?<separator>:)"
Статья довольно хорошо объясняет это. Оттуда вы просто начинаете есть токены, как считаете нужным.
Подсказка: для парсера LL(1) (читай: легко) токены не могут иметь общий префикс. Если у вас есть abc
в качестве знака, вы не можете иметь ace
как знак
Примечание: в статье отсутствует | символы в его примерах, просто добавьте их.
Существует еще одна библиотека YAML для.NET, которая находится в стадии разработки. Прямо сейчас он поддерживает чтение потоков YAML и был протестирован на Windows и Mono. Поддержка записи в настоящее время осуществляется.
Использование библиотеки почти всегда предпочтительнее, чем использование собственных. Вот краткий список пунктов "О, я никогда не буду нуждаться в этом / я не думал об этом", которые в конечном итоге придут к вам позже:
- Спасаясь от персонажей. Что если вы хотите: в ключе или] в значении?
- Спасаясь от побега персонажа.
- Unicode
- Сочетание табуляции и пробелов (см. Проблемы с синтаксисом, чувствительным к пробелам в Python)
- Обработка различных форматов возвращаемых символов
- Обработка сообщений об ошибках синтаксиса
Как и предполагали другие, YAML выглядит как ваша лучшая ставка.
Мне кажется, что вам лучше использовать конфигурационный файл на основе XML, поскольку уже есть классы.NET, которые могут относительно легко считывать и хранить информацию для вас. Есть ли причина, по которой это невозможно?
@Bernard: Это правда, что ручное редактирование XML утомительно, но структура, которую вы представляете, уже очень похожа на XML.
Тогда да, есть хороший метод там.
@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, ']');
}
Вы также можете использовать стек и использовать алгоритм 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";
}
Вы, вероятно, можете адаптироваться, чтобы вы могли прочитать содержимое вашего файла. Регулярные выражения также хорошая идея.
Независимо от сохраняемого формата использование Regex будет самым быстрым способом анализа. В ruby, вероятно, будет несколько строк кода.
\[KEY:(.*)\]
\[SUBKEY:(.*)\]
Эти два получили бы значение и SubValue в первой группе. Проверьте MSDN о том, как сопоставить регулярное выражение со строкой.
Это то, что каждый должен иметь в своем котенке. Дни до Регекса выглядели как Ледниковый период.