Необходимо мне в своем коде парсить строки вида "A=1; B=122; c=x=2,у=3" в словарь, в данном случае должно получиться такое:
A: 1
B: 122
c: x=2,y=3.
Захотел поначалу все это реализовать в виде функции, а теперь задумался — зачем изобретать велосипед, наверняка должны быть какие-то готовые функции в .net. Курил MSDN, нашел какие-то функции расширения класса string, — ToDictionary(...), — посмотрел я их, но там как-то все сложно и непонятно, какие-то аргументы типа IENumerable зачем-то. В общем, не понял я, можно ли их прикрутить для решения моей задачи.
Собственно, вопрос: есть ли какая-нибудь встроенная функция в дот-нете, которая делает subj, и которой можно управлять заданием двух массивов char: один содержит разделители элементов конфига в строке (в данном случае — единственный разделитель ';'), второй — разделители ключа и значения (если в одном элементе их несколько, то второй и последующие являются частью значения, как видно из приведенного выше примера). Ну, или, хотя бы часть этой работы. Поделитесь опытом, как бы вы реализовали такую штуку.
Re: Распарсить строку в Dictionary<string, string>
Здравствуйте, teapot2, Вы писали:
T>Добрый день, друзья.
T>Необходимо мне в своем коде парсить строки вида "A=1; B=122; c=x=2,у=3" в словарь, в данном случае должно получиться такое:
T>A: 1 T>B: 122 T>c: x=2,y=3.
string s = "A=1; B=122; c=x=2,у=3";
var dictionary = s.Split(';').ToDictionary(pair => pait.Split('=')[0], pair => pait.Split('=')[1]);
Синтаксис не проверял, если что
Re: Распарсить строку в Dictionary<string, string>
Здравствуйте, teapot2, Вы писали:
T>Собственно, вопрос: есть ли какая-нибудь встроенная функция в дот-нете, которая делает subj, и которой можно управлять заданием двух массивов char: один содержит разделители элементов конфига в строке (в данном случае — единственный разделитель ';'), второй — разделители ключа и значения (если в одном элементе их несколько, то второй и последующие являются частью значения, как видно из приведенного выше примера). Ну, или, хотя бы часть этой работы. Поделитесь опытом, как бы вы реализовали такую штуку.
Можно еще регуляркой попробовать:
public static Dictionary<string, string> ParseConfig(string Config, string elementSeparators, string valueSeparators)
{
Dictionary<string, string> result = new Dictionary<string, string>();
string re = "((([^" + elementSeparators + "=]+)[" + valueSeparators + "]([^" + elementSeparators + "]+))[\\s]*)";
Match m = Regex.Match(Config, re);
while (m.Groups.Count == 5)
{
result.Add(m.Groups[3].Value, m.Groups[4].Value);
m = m.NextMatch();
}
return result;
}
Re[2]: Распарсить строку в Dictionary<string, string>
Насколько усложнится эта схема, если мы захотим управлять инцидентами с возможным дублированием ключей в списке, предусмотрев один из трех вариантов реакции:
— бросать исключение;
— сохранять первый встретившийся элемент;
— каждый раз перезаписывать элемент с таким же ключом (т.е. в итоге сохранится последний);
Скажем, предусмотрев дополнительный параметр такого типа:
Здравствуйте, QrystaL, Вы писали:
QL>Как-то так:
QL>
QL>public static IDictionary<String, String> ParseToDictionary(String input, OnDuplicateKey strategy)
QL>{
QL> var delims1 = new[] { ';' };
QL> var delims2 = new[] { '=' };
QL> var preResult = input.Split(delims1)
QL> .Select(v => v.Split(delims2, 2))
QL> .ToLookup(v => v[0].Trim(), v => v[1].Trim());
QL> switch (strategy)
QL> {
QL> case OnDuplicateKey.RaiseError:
QL> return preResult.ToDictionary(g => g.Key, g => g.Single());
QL> case OnDuplicateKey.SaveFirst:
QL> return preResult.ToDictionary(g => g.Key, g => g.First());
QL> case OnDuplicateKey.Rewrite:
QL> return preResult.ToDictionary(g => g.Key, g => g.Last());
QL> default:
QL> return null;
QL> }
QL>}
QL>
Спасибо. Уже кое-что. Но и с этим кодом есть проблемы.
1. Он "падает" на "пустых" конфигурационных элементах. Конечно, ситуацию двух разделителей подряд (когда в строке встретится такое "...;;...") пофиксить достаточно просто, надо вызвать метод split с соответствующей опцией:
...
var delims1 = new[] { ';' };
var delims2 = new[] { '=' };
var preResult = input.Split(delims1, StringSplitOptions.RemoveEmptyEntries)
...
Но этот трюк "обламывается", если между разделителями встретится пробельный символ: "...; ;..."
Как отфильтровывать пустые элементы?
2. Эстетический момент. Можно ли обойтись без switcha'а в коде — а то как-то не вполне кошерно "функционально" получается? ЕМНИП, где-то на Хабре мне встречалось описание аналогичного случая — когда с использованием трюка на лямбдах удавалось убрать switch из кода, заменив его выборкой из предварительно составленного словаря, ключом которого был "аргумент" switch, а значением метод.
Re[5]: Распарсить строку в Dictionary<string, string>
Вы писали:
QL>Вы ведь не думаете, что на форуме вам напишут готовый production код?
Ну до production тут еще весьма далеко. С другой стороны, production code я все-таки написал — не в функциональном стиле. В чисто академических целях хочется довести до такого же состояния и код в функциональном стиле, чтобы посмотреть, действительно ли он будет более простым и понятным. Разумеется, на форуме мне его не напишут, но помочь в тех вопросах, которыми я владею не очень уверенно, могут весьма существенно. Что, собственно, и происходит — за это спасибо.
QL>По поводу остальных вопросов:
QL>
Кажется, потихоньку начинаю въезжать. В приведенном варианте.Where мне не понравилось вот что: он игнорирует элементы конфигурации типа ключ-без-начения ("... ; a ; ..."). Более логично для таких элементов бросать исключение (как я понимаю, для этого достаточно заменить ==2 на >= 1 в .Where, но это не наш путь — см. ниже). Сам я написал указанный фрагмент следующим образом:
Кстати, в продакшене исключения надо кидать свои, а не полагаться на системные. Следующая задача — прикрутить сюда проверку и выбрасывание своих исключений. На своем текущем уровне понимания вопроса я бы сделал это как-то так:
Определил бы свою функцию-проверяльщик
public static class Utility
{
public static bool ValidateKeyAndValue(this string[] sa)
{
// assert(sa.Length == 1 || sa.Length == 2)if (string.IsNullOrEmpty(sa[0])) throw new Exception("Не задан ключ в элементе конфигурации");
if (string.IsNullOrEmpty(sa[1])) throw new Exception("Не задано значение для ключа " + sa[0]);
return true;
}
}
Можно ли это сделать более изящно/правильно/эффективно?
Дальше нам предстоит внести в этот код управление выкидыванием исключения в зависимости от значения флага.
Re: Распарсить строку в Dictionary<string, string>
Здравствуйте, teapot2, Вы писали:
T>Добрый день, друзья.
T>Необходимо мне в своем коде парсить строки вида "A=1; B=122; c=x=2,у=3" в словарь, в данном случае должно получиться такое:
T>A: 1 T>B: 122 T>c: x=2,y=3.
T>Захотел поначалу все это реализовать в виде функции, а теперь задумался — зачем изобретать велосипед, наверняка должны быть какие-то готовые функции в .net. Курил MSDN, нашел какие-то функции расширения класса string, — ToDictionary(...), — посмотрел я их, но там как-то все сложно и непонятно, какие-то аргументы типа IENumerable зачем-то. В общем, не понял я, можно ли их прикрутить для решения моей задачи.
T>Собственно, вопрос: есть ли какая-нибудь встроенная функция в дот-нете, которая делает subj, и которой можно управлять заданием двух массивов char: один содержит разделители элементов конфига в строке (в данном случае — единственный разделитель ';'), второй — разделители ключа и значения (если в одном элементе их несколько, то второй и последующие являются частью значения, как видно из приведенного выше примера). Ну, или, хотя бы часть этой работы. Поделитесь опытом, как бы вы реализовали такую штуку.
В связи с тем, что Ваши аппетиты растут не по дням, а по часам, я бы предложил Вам использовать какой-нибудь Coco/R.
Вполне понятная грамматика, легко поддерживать и расширять, compile-time генерация кода парсера.
Respectfully,
Alexander Fedin.
Re[2]: Распарсить строку в Dictionary<string, string>
От:
Аноним
Дата:
22.05.13 10:01
Оценка:
Здравствуйте, alexanderfedin, Вы писали:
A>В связи с тем, что Ваши аппетиты растут не по дням, а по часам, я бы предложил Вам использовать какой-нибудь Coco/R. A>Вполне понятная грамматика, легко поддерживать и расширять, compile-time генерация кода парсера.
Это даже не "стрелять из пушки по воробьям". Это "запустить по воробъям МБР с РГЧ" (по одной "Ч" на каждого воробья ). Мой "наколеночный" (но полностью рабочий ибо оттестирован) код содержит менее сотни строк, половина из которых содержит только одну фигурную скобку. Полностью написан, отлажен и оттестирован за пару-тройку часов. Просто изначально я хотел избежать затрат этого времени. Но не вышло. Боюсь, предлагаемый Вами вариант потребует трудозатрат, больших на порядки.
Re[3]: Распарсить строку в Dictionary<string, string>