Russian Qt Forum

Qt => Общие вопросы => Тема начата: titan83 от Январь 18, 2016, 17:29



Название: Распарсить строку INI-файла
Отправлено: titan83 от Январь 18, 2016, 17:29
Коллеги. Полдня парюсь - не могу сделать Regexp для того, чтобы парснуть такую строку:
Name = Value ; some comment
В результате хочу иметь QVariantList, где имя -> Name, а значение -> Value. Коммент должен игнорироваться.
Сейчас использую такую строку:
^\s*([^(\s*\=\s*)]+)\s*\=\s*([\d\D]*)
Строку без комментария в конце она парсит правильно, а вот дальше добавить отсечение того, что после ';' - не получается.
Буду очень признателен, если кто-то подскажет.
Спасибо.


Название: Re: Распарсить строку INI-файла
Отправлено: gil9red от Январь 18, 2016, 17:43
^(\w+) *= *(\w+) *; *.*$

Ссылка на тестер: https://regex101.com/r/rU0jK4/1


Название: Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 19, 2016, 10:25
Ах как активно, рьяно это заучивается  :)


Название: Re: Распарсить строку INI-файла
Отправлено: kambala от Январь 19, 2016, 14:26
в данном случае без регулярок будет сделать проще, понятнее и быстрее


Название: Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 19, 2016, 14:41
в данном случае без регулярок будет сделать проще, понятнее и быстрее
Хотел сказать то же самое - но уже боюсь трогать "священную корову" (затравили  :))


Название: [Решено] Re: Распарсить строку INI-файла
Отправлено: titan83 от Январь 26, 2016, 13:20
gil9red, спасибо за ответ.
Переключался на другую задачу, поэтому тут затормозил.

мой результат такой:
^\s*(;?)\s*(\S+)\s*=\s*\"?([^;"]+[()\w+\s*]+)\"?\s*;?
Этот рекэкс парсит и комментарий с начала строки ("; Test1 = Value1"),
игнорирует комментарии в той же строке после имени-значения ("Test1 = Value1 ; comment") при этот комментарий может как присутствовать, так и отсутствовать.
Также значение можно писать, как в кавычках ("Value1"), так и без кавычек (Value1).
Комментарий в конце строки игнорируется.
Т.е. в итоге получаю три группы:
1. Символ ';' или '', если символ есть, значит вся строка комментарий.
2. Название параметра.
3. Значение параметра.

На мой взгляд получилось более чем достойно.

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

Всем еще раз спасибо за участие.


Название: Re: [Решено] Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 26, 2016, 14:21
Также значение можно писать, как в кавычках ("Value1"), так и без кавычек (Value1).
На этом я неск раз залетал - возможны "кривые" кавычки, смотрятся почти также но символ другой

мой результат такой:
^\s*(;?)\s*(\S+)\s*=\s*\"?([^;"]+[()\w+\s*]+)\"?\s*;?
...
На мой взгляд получилось более чем достойно.
Ой  :)


Название: Re: Распарсить строку INI-файла
Отправлено: gil9red от Январь 26, 2016, 14:24
А не проще использовать не свой формат, а стандартный? Тот же xml или json


Название: Re: Распарсить строку INI-файла
Отправлено: kambala от Январь 26, 2016, 15:49
в данном случае без регулярок будет сделать проще, понятнее и быстрее
Не согласен: регулярки - суть готовый автомат состояний, да они сложны для понимания, особенно по началу, но результат того стоит.
я их знаю достаточно неплохо, но по-моему очевидно, что в данном случае они вообще не к месту, т.к. только усложняют читабельность (ты вон аж сколько комментариев написал к своей регулярке). изучить новую технологию и совать ее во все подряд — это неправильный подход.

вот скажи: сколько ты потратил времени на то, чтобы составить свою регулярку? а сколько бы ты потратил времени на следующий код, делающий то же самое?
Код
C++ (Qt)
QString input;
QString nameValue = input.split(";").at(0).trimmed();
if (!nameValue.isEmpty())
{
   QStringList nameValueList = nameValue.split(" = ");
   QString name = nameValueList.at(0), value = nameValueList.at(1);
}


Название: Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 26, 2016, 16:49
вот скажи: сколько ты потратил времени на то, чтобы составить свою регулярку? а сколько бы ты потратил времени на следующий код, делающий то же самое?
Код
C++ (Qt)
QString input;
QString nameValue = input.split(";").at(0).trimmed();
if (!nameValue.isEmpty())
{
   QStringList nameValueList = nameValue.split(" = ");
   QString name = nameValueList.at(0), value = nameValueList.at(1);
}
Ну тут можно предъявить немало претензий

1) строка может быть и пустой (летим на первой строчке)
2) а если nameValue.isEmpty() вернет true (не ошибка, но нужен еще код)
3) никто не обещал что будут пробелы до и после "="
4) знака равенства может вообще не быть (летим на последней строке)
5) возможны доп пробелы, напр "  = "  (нужен еще trimmed)
6) все-таки копирование интенсивное (хотя в UI это терпимо)

Думается такие "противные мелочи" (постоянно вылазят, доделывать) и побуждают молодых людей юзать "регулярки". А дальше срабатывает инерция: "я же не даром учил!"

...изучить новую технологию...
Совершенно верно, "технология" - и НИКАКОГО отношения к программированию она не имеет. Может знание этой технологии и добавит программисту "очков" (напр при приеме на работу) - но может и нет.

А ведь аккуратный разбор (без всяких регулярок) - отличная тренировка логического мЫшления. Напр что если в стиле wxWidgets (в одном проекте с ним работал и тамошний строчник сподобався)
Код
C++ (Qt)
void ParseIniLine( const QString & txt, QStringRef result[2] )
{
QStringRef src = BeforeFirst(txt, ';').trimmed();
result[0] = BeforeFirst(src, '=').trimmed();
result[1] = AfterFirst(src, '=').trimmed();
}
 
Вроде ни одного malloc :)  Да, придется написать пару однострочных inline, но они всегда нужны.


Название: Re: Распарсить строку INI-файла
Отправлено: kambala от Январь 26, 2016, 17:03
0) суть же была не в том, чтобы в мельчайших подробностях написать код :)
1) как мы полетим, если после сплита получится список из одного элемента — пустой строки?
2) само собой, но я не знаю как автор такое обрабатывает
3) автор нам обещает. Тут (как и в случае с ; ) можно разбить по \s*=\s*
4) опять же автор нам обещает. но тут мы и правда полетим при доступе к элементу с индексом 1
5) см. 0


Название: Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 26, 2016, 17:12
3) автор нам обещает. ..
4) опять же автор нам обещает ..
Не надо быть таким доверчивым :) А вообще Ваш новый ход мысли мне нравится (раньше тоже были регулярки на любой чих)


Название: Re: Распарсить строку INI-файла
Отправлено: Old от Январь 26, 2016, 17:13
А ведь аккуратный разбор (без всяких регулярок)
...
Вроде ни одного malloc :) 
Ну я бы не назвал это аккуратным разбором.
malloc там может быть и нет, но строка разбирается в 3 (три) прохода, хотя легко разбирается и в один.


Название: Re: Распарсить строку INI-файла
Отправлено: kambala от Январь 26, 2016, 17:46
А вообще Ваш новый ход мысли мне нравится (раньше тоже были регулярки на любой чих)
если регулярное выражение несложное для написания и понимания, лучше использовать его. здесь как раз не такой случай.


Название: Re: Распарсить строку INI-файла
Отправлено: kai666_73 от Январь 26, 2016, 18:43
Подолью масла в огонь:
Код:
boost::property_tree::ini_parser::read_ini


Название: Re: Распарсить строку INI-файла
Отправлено: titan83 от Январь 27, 2016, 16:24
)))
Не ожидал - тут даже мини холиварчик наметился))
Хотя доля правды в словах про технологию и очки есть, мне кажется ;)
На самом деле все затеивается для возможности ЗАПИСАТЬ ini из программы, не меняя его структуру.
Т.е. для read-only ini меня полностью устраивает функционал QSettings, но если меняешь какое-нибудь значение из программы, то QSettings уберет все комментарии, перетасует все поля и будет в своем праве ;)
А у меня ini еще и людьми читаются, поэтому, кстати, я и не использую xml - формат супер, но для целевой аудитории будет сложноват. Я в xml храню всю информацию о ini-файле - какие поля должны присутствовать, какие в них допустимы значения, как отображать названия полей в GUI - это удобно.


Название: Re: Распарсить строку INI-файла
Отправлено: titan83 от Январь 27, 2016, 16:28
На счет QString.split() - в начале своего пути я именно им и пользовался, вроде, все работает, но шаг в сторону - расстрел, тьфу - segfault, а если не хочешь segfault'ов, то изволь обложить все if-then, и вот тут понимаешь, что уже все не так и просто и элегантно.
Что бы тут не говорили старшие товарищи, но я пока что считаю regexp вполне удобным инструментом для работы с текстом, лишенным многих недостатков split().


Название: Re: Распарсить строку INI-файла
Отправлено: Racheengel от Январь 27, 2016, 16:30
а если строка будет типа

Key = "String1; String2;" ; blabla

то надо учитывать и комменты межды кавычками...


Название: Re: Распарсить строку INI-файла
Отправлено: titan83 от Январь 27, 2016, 18:30
а если строка будет типа

Key = "String1; String2;" ; blabla

то надо учитывать и комменты межды кавычками...

epic fail - вы правы)))

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


Название: Re: Распарсить строку INI-файла
Отправлено: Igors от Январь 28, 2016, 08:37
На счет QString.split() - в начале своего пути я именно им и пользовался, вроде, все работает, но шаг в сторону - расстрел, тьфу - segfault, а если не хочешь segfault'ов, то изволь обложить все if-then, и вот тут понимаешь, что уже все не так и просто и элегантно.
Возможно QString::split - худшее что есть в Qt. С точки зрения производительности это ужасно, но главное -такие ф-ции развращают пользующегося. Немного подумать и сделать (ведь задачка-то в общем для начинающих) ему уже в лом, хочется вот так ляпнуть контейнером - и все готово.

Что бы тут не говорили старшие товарищи, но я пока что считаю regexp вполне удобным инструментом для работы с текстом, лишенным многих недостатков split().
Человек недавно потративший время/силы на регулярки иначе считать не может - это нормально, должно пройти время (может значительное) для "переоценки ценностей". Поэтому "пока что" (ростки демократии) - уже хорошо  :)

[off]Респект модератору, правильно убрал грубияна с яичками [/off]