Russian Qt Forum

Qt => Общие вопросы => Тема начата: m_ax от Май 30, 2011, 23:24



Название: regexp для одной простой задачи
Отправлено: m_ax от Май 30, 2011, 23:24
Приветствую)

Разбираюсь сейчас с регулярными выражениями. Хочу спросить совета.

Дело в следующем. Имеется текст вида (LaTeX):

... Note that the spectacular reentrant superconductivity predicted in 1997~\cite{Khusainov_97, Khusainov_97_PRB} has been recently observed experimentally in the Nb/CuNi bilayer~\cite{Zdravkov_06}...
Мне нужно составить список всех ссылок, что встречаются в тексте.
Ссылки (в данном текте их всего три: Khusainov_97, Khusainov_97_PRB, Zdravkov_06) находятся внутри команд \cite{...}. Причём их колличество (ссылок) не ограниченно, но не меньше одной и они разделяются запятыми.
Сама ссылка может содержать  цифры, нижнее подчёркивание, ну короче \w

Вот что-то не соображу, как написать одно рег. выражение, чтоб енто сотворить.

Сейчас я использую следующую (рабочую) схему:
Код
C++ (Qt)
   QRegExp rx("cite\\{([\\w\\s,]*)\\}"); /* Вытаскивает всё содержимое команды \cite{} */
   QRegExp rxAuthorRef("\\b(\\w+)\\b"); /* Из этого всего вытаскивает сами ссылки */
   QString str("Note that the spectacular reentrant superconductivity"
               "predicted in 1997~\cite{Khusainov_97, Khusainov_97_PRB}"
               "has been recently observed experimentally in the Nb/CuNi"
               "bilayer~\cite{Zdravkov_06}.");
 
   QStringList authorsRef; /* Список всех ссылок */
   int pos = 0;
   while (pos >= 0) {
       if ((pos = rx.indexIn(str, pos)) >= 0)  {
           QString s = rx.cap(1); /* s - содержит всё содержимое команды \cite{} */
           int pos2 = 0;
           while (pos2 >= 0) { /* В этом цикле извлекаем сами ссылки */
               if ((pos2 = rxAuthorRef.indexIn(s, pos2)) >= 0) {
                   QString author = rxAuthorRef.cap(1);
                   authorsRef.append(author); /* засовываем их в список */
                   pos2 += author.size();
               }
           }
           pos += s.size();
       }
   }
   authorsRef.removeDuplicates();
   qDebug() << authorsRef;
 
 

Этот вариант работает, но требует двух regexp'ов (и два цикла).
Хотелось бы реализовать это по-умному.
Как это сделать? В смысле, как написать один regexp, который всё бы это делал?


Название: Re: regexp для одной простой задачи
Отправлено: kambala от Май 30, 2011, 23:45
Код
C++ (Qt)
QRegExp rx("\\\\cite\\{(.+)\\}");
QStringList authorsList;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1)
{
   authorsList << rx.cap(1).split(", ");
   pos += rx.matchedLength();
}
вроде так

и у тебя кстати в str бэкслэши не двойные ;)


Название: Re: regexp для одной простой задачи
Отправлено: m_ax от Май 30, 2011, 23:54
Код
C++ (Qt)
QRegExp rx("\\\\cite\\{(.+)\\}");
QStringList authorsList;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1)
{
   authorsList << rx.cap(1).split(", ");
   pos += rx.matchedLength();
}
вроде так

и у тебя кстати в str бэкслэши не двойные ;)
Спасибо. Правда это даёт несколько иной результат:
Код
C++ (Qt)
("Khusainov_97", "Khusainov_97_PRB}has been recently observed experimentally in the Nb/CuNibilayer~\cite{Zdravkov_06")
 
Вместо
Код
C++ (Qt)
("Khusainov_97", "Khusainov_97_PRB", "Zdravkov_06")
 
По-поводу не двойных бэкслэшей: я буду загружать файл (.tex) в котором все команды начинается с одинарного бэкслэша.


Название: Re: regexp для одной простой задачи
Отправлено: kambala от Май 31, 2011, 00:05
а, значит надо использовать "нежадный" захват ".+?". как он тут пишется...
Код
C++ (Qt)
QRegExp rx("\\\\cite\\{(.+)\\}");
rx.setMinimal(true);


Название: Re: regexp для одной простой задачи
Отправлено: blood_shadow от Май 31, 2011, 00:07
я бы использовал форвард референсес
можно усовершенствовать:
Код
C++ (Qt)
QRegExp rx("cite\\{[^\\}](?=\\})");
QStringList authorsList;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1)
{
   authorsList << rx.cap(1).split(", ");
   pos += rx.matchedLength();
}


Название: Re: regexp для одной простой задачи
Отправлено: m_ax от Май 31, 2011, 00:23
я бы использовал форвард референсес
можно усовершенствовать:
Код
C++ (Qt)
QRegExp rx("cite\\{[^\\}](?=\\})");
QStringList authorsList;
int pos = 0;
while ((pos = rx.indexIn(str, pos)) != -1)
{
   authorsList << rx.cap(1).split(", ");
   pos += rx.matchedLength();
}

Не работает  :(
Говорит - список пуст.
Да, забыл сказать, что между ссылками и между ссылкой и запятой и между {} может быть любое число пробелов. И соответственно список должен содержать ссылки без пробелов. Мелочь конечно, но эт ещё один цикл по списку...


Название: Re: regexp для одной простой задачи
Отправлено: kambala от Май 31, 2011, 00:32
Не работает  :(
нежадный захват постом выше работает - проверено ;)
Да, забыл сказать, что между ссылками и между ссылкой и запятой и между {} может быть любое число пробелов. И соответственно список должен содержать ссылки без пробелов. Мелочь конечно, но эт ещё один цикл по списку...
да какой цикл :)
Код
C++ (Qt)
authorsList << rx.cap(1).split(QRegExp(",\\s*"));


Название: Re: regexp для одной простой задачи
Отправлено: m_ax от Май 31, 2011, 00:42
Не работает  :(
нежадный захват постом выше работает - проверено ;)
Да, забыл сказать, что между ссылками и между ссылкой и запятой и между {} может быть любое число пробелов. И соответственно список должен содержать ссылки без пробелов. Мелочь конечно, но эт ещё один цикл по списку...
да какой цикл :)
Код
C++ (Qt)
authorsList << rx.cap(1).split(QRegExp(",\\s*"));
Да, этот вариант работает)
Правда, если между первой { и последующей ссылкой идут пробелы то.. :(
В смысле если будет так: \cite{     Ref1,  Ref2}


Название: Re: regexp для одной простой задачи
Отправлено: m_ax от Май 31, 2011, 00:46
Цитировать
В смысле если будет так: \cite{     Ref1,  Ref2}
Понял.. Тогда нужно так:
Код
C++ (Qt)
QRegExp rx("cite\\{\\s*([^\\s].+)\\}");
 


Название: Re: regexp для одной простой задачи
Отправлено: kambala от Май 31, 2011, 00:49
ну тогда придется еще trimmed() вызвать для первой строки в списке от split(). или твой вариант (вроде должен сработать).


Название: Re: regexp для одной простой задачи
Отправлено: m_ax от Май 31, 2011, 01:02
Всем спасибо)
Пожалуй оставлю вариант kambala с дополнением
Код
C++ (Qt)
QRegExp rx("cite\\{\\s*([^\\s].+)\\}");