На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: ALXR
  
    > Извлечение неопределенного числа подпоследовательностей , или парсинг XML со всеми атрибутами
      Здравствуйте!

      Парсю XML на Java.
      К вебу отношение очень слабое, но задаю вопрос здесь, т.к. вопрос именно по регуляркам.
      Стоит задача построить дерево элементов, со всеми атрибутами.

      Есть регулярка поиска тегов, открывающих и закрывающих, с атрибутами:

      [^<]*\<\s*(/?[\w]+)(\s+([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'))*\s*(\s/)?\>

      Для каждого тега есть подвыражение, которое должно вычленять атрибуты, которых неизвестное кол-во:

      (\s+([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'))*

      Упростим его:

      (\s+[\w-]+=("[^"]*"|'[^']*'))*

      Суть в том, что извлекается только последняя пара имя_атрибута = заначение, а не все.

      Когда первый раз такая задача встала, она была решена так (парсился URL):

      ((name1|name2|name3| ... |nameN)/([^/]+)/)*

      Заранее известны были имена параметров, поэтому можно было воспользоваться тем, что если в параметрах есть |, то выбираются все разные последние соответствия.

      Как можно перестроить регулярку, не зная заранее имена атрибутов?

      Спасибо.
        Цитата
        (\s+[\w-]+=("[^"]*"|'[^']*'))*

        Суть в том, что извлекается только последняя пара имя_атрибута = заначение, а не все.

        А каким образом ты пытаешься получить все эти пары? Как, по-твоему, это должно работать? И что на этот счет говорит документация?
          ExpandedWrap disabled
            String sp = ""
                            
                            + "[^<]*"
                            + "<"
                            + "\\s*"
                            + "(/?[\\w]+)"
                            
                            //+ "("
                            + "("
                            
                            + "\\s+"
                            + "([\\w-]+)"
                            
                            + "\\s*"
                            + "="
                            + "\\s*"
                            
                            + "("
                            + "\"([^\"]*)\""
                            + "|"
                            + "'([^']*)'"
                            + ")"
                            
                            + ")*"
                            //+ ")"
                            
                            + "\\s*"
                            + "(\\s/)?"
                            + ">"
                            ;
                    //s = "</div>";
                    Pattern p = Pattern.compile(sp/*, Pattern.MULTILINE */);
                                System.out.println(p.pattern()); // tag name
             
                    Matcher m = p.matcher(s);
                    while (m.find()) {
                        System.out.println(m.group(1)); // tag name
                        for (int i = 0; i < m.groupCount(); i++) {
                            System.out.println(m.group(i));
                        }
                        System.out.println("-------------------end\n\n");
                    }


          Проверил на одной из страниц этого форума, изменив следующий фрагмент (измененная строка обособлена):

          ExpandedWrap disabled
            s = "<script type='text/javascript' src='http://forum.sources.ru/html/jqcd/jqcd.js'></script>\n" +
            "<script type='text/javascript' src='http://forum.sources.ru/html/global.js?15'></script>\n" +
            "\n" +
            "<table id='b-header' border='0' width='100%' cellspacing='0' cellpadding='1'>\n" +
            "<tr id='logostrip'>\n" +
             
             
            "<td class='b-logo-wrapper' id = \"test-id\" data-one='one' data-two=\"two\"><a class='e-logo-link' href='http://www.sources.ru' title='На главную'><img class='e-logo-img' src='style_images/1/logo4.gif' alt='На главную' border='0'></a></td>\n" +
             
             
            "\n" +
            "<td align='center' class='b-slogan-wrapper'>\n" +
            "<!-- SLOGAN -->\n" +
            "</td>\n" +
            "\n";


          результат:

          Цитата

          td

          <td class='b-logo-wrapper' id = "test-id" data-one='one' data-two="two">
          td
          data-two="two"
          data-two
          "two"
          two
          one
          -------------------end


          1) Записано только последнее подвыражение (two)
          2) Предпоследнее (one) попало только как значение оказалось в результате потому. что оно стоит в других кавычках, а там идеи ИЛИ для каждых кавычек своим значением, потому что с каждыми кавычками получается одно уникальное подвыражение.

          В документации java по этому поводи не нашел ничего, но на PHP с тем же столкнулся и тогда нашел где-то, уже не помню где, что это фича.
            Цитата
            (\s+([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'))*

            Разъясняю "на пальцах":

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

            В найденном наборе для каждой подмаски захватывается только одна подстрока, т. е. из всех пар имя=значение возвращается только одна пара. Почему именно последняя? Потому что так проще реализовать - меньше лишних проверок.

            В общем, все логично и ожидаемо. Что делать? Очень просто: вырезать найденный тег в отдельную строку, а затем на эту строку натравить ([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'), и тогда последовательный поиск выдаст все нужные пары.

            P. S. Я с явой не знаком, деталей реализации регвыров на ней не знаю, если в чем-то ошибся - поправьте.
              Это понятно, с этим смирился уже. Вопрос в том, как переделать регулярку, чтобы извлекать тег и его атрибуты сразу, а не в два прохода, потому что так быстрее.
              Придумал костыльное решение на ограниченное число параметров: повторение n раз в выражении фрагмента

              (\s+([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'))?

              - уже с '?', т.е. 0 или 1 раз, а после этого поставил

              ((\s+([\w-]+)\s*=\s*("([^"]*)"|'([^']*)'))*)

              - вычленитель строки с оставшимися атрибутами, чтобы сохранить и потом допарсить. Получил желаемый результат, можно пройтись циклом и смэпить имя=>значение, но это только для максимум n атрибутов.

              Добавлено
              результат:

              Цитата
              <td class='b-logo-wrapper' id = "test-id" data-one='one' data-two="two">
              td
              class='b-logo-wrapper'
              class
              'b-logo-wrapper'
              null
              b-logo-wrapper
              id = "test-id"
              id
              "test-id"
              test-id
              null
              data-one='one'
              data-one
              'one'
              null
              one
              data-two="two"
              data-two
              "two"
              two
              null
              null
              null
              null
              null
              null
              null
              null
              null
              null
              null

              null
              null
              null
              null
              null
              -------------------end
                Цитата
                Вопрос в том, как переделать регулярку, чтобы извлекать тег и его атрибуты сразу, а не в два прохода

                Как бы переделать паровоз в космический корабль? Никак. Регвыры не всесильны, и, представь себе, есть такие задачи, для решения которых регвыры непригодны.

                Хотя, чисто теоретически, возможно, в какой-нибудь реализации регвыров есть способ захватывать для подмаски список, а не единственную подстроку. Но это уже извращение.
                  Пока вопрос решен повтором подмаски для одного атрибута вручную с квантификатором {0,1}, а что не уместилось в кол-во повтором будет допарсиваться позже отдельно.
                  0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                  0 пользователей:


                  Рейтинг@Mail.ru
                  [ Script execution time: 0,0316 ]   [ 16 queries used ]   [ Generated: 28.03.24, 20:43 GMT ]