Java регулярное выражение для соответствия всем элементам HTML, кроме одного особого случая - PullRequest
2 голосов
/ 20 августа 2009

У меня есть строка с некоторой разметкой, которая выглядит следующим образом:

The quick brown <a href="www.fox.org">fox</a> jumped over the lazy <a href="entry://id=6000009">dog</a> <img src="dog.png" />.

Я пытаюсь убрать все, кроме якорных элементов с "entry: // id =" внутри. Таким образом, желаемый результат из приведенного выше примера будет:

The quick brown fox jumped over the lazy <a href="entry://id=6000009">dog</a>.

Написание этого матча, самое близкое, что я до сих пор прошел:

<.*?>!<a href=\"entry://id=\\d+\">.*?<\\/a>

Но я не могу понять, почему это не работает. Любая помощь (кроме «почему вы не используете парсер» :) будет принята с благодарностью!

Ответы [ 3 ]

7 голосов
/ 20 августа 2009

Я бы на самом деле не использовал регулярные выражения для разбора HTML. HTML не является регулярным, и нет никаких крайних случаев, чтобы вас сбить с толку.

Проверьте JTidy вместо.

1 голос
/ 20 августа 2009

Используя это:

((<a href="entry://id=\d+">.*?</a>)|<!\[CDATA\[.*?\]\]>|<!--.*?-->|<.*?>)

и объединение его с заменой всех $ 2 будет работать для вашего примера. Код ниже подтверждает это:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static org.junit.Assert.*;
import org.junit.Test;


public class TestStack1305864 {

    @Test
    public void matcherWithCdataAndComments(){
        String s="The quick <span>brown</span> <a href=\"www.fox.org\">fox</a> jumped over the lazy <![CDATA[ > ]]> <a href=\"entry://id=6000009\">dog</a> <img src=\"dog.png\" />.";
        String r="The quick brown fox jumped over the lazy <a href=\"entry://id=6000009\">dog</a> .";
        String pattern="((<a href=\"entry://id=\\d+\">.*?</a>)|<!\\[CDATA\\[.*?\\]\\]>|<!--.*?-->|<.*?>)";
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(s);

        String t = s.replaceAll(pattern, "$2");
        System.out.println(t);
        System.out.println(r);
        assertEquals(r, t);
    }
}

Идея состоит в том, чтобы собрать все интересующие вас элементы в определенной группе, чтобы вы могли вставить их обратно в строку.
Таким образом, вы можете заменить все:
Для каждого элемента, который не соответствует интересным, группа будет пустой, и элемент будет заменен на ""
Для интересных элементов группа не будет пустой и будет добавлена ​​к результирующей строке.

edit: обрабатывать вложенные <или> в CDATA и комментарии
редактирование: см. http://martinfowler.com/bliki/ComposedRegex.html для шаблона композиции регулярного выражения, разработанного, чтобы сделать регулярное выражение более читабельным для целей обслуживания.

1 голос
/ 20 августа 2009

Не легко возможно с регулярным выражением. Я рекомендую парсер, который понимает семантику HTML / XML.

Если вы настаиваете , вы можете сделать многоэтапный подход, например:

  • Заменить "<(a\s*href="entry:.*?/a)>" на "{{{{\1}}}}"
  • Заменить "<(?!/a}}}})[^>]*>" на ""
  • Заменить "{{{{" на "<"
  • Заменить "}}}}" на ">"

Имейте в виду, что вышеописанное может привести к ошибкам и в какой-то момент завершится ошибкой. Считай это уродливым взломом, а не реальным решением. Нечто подобное вышесказанному подходит для однократного редактирования некоторого текстового файла в текстовом редакторе с поддержкой регулярных выражений, но для повторного, реального использования в качестве части обработки данных в приложении - не так уж много.

...