Почему java.util.regex.matcher не соответствует всем экземплярам в этой строке?

У меня есть следующий код: http://ideone.com/mFUaqG

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

class RegexUtils
{
    private static final Pattern resourceURLCSS     = Pattern.compile("url\\([\'\"](((?!://).)*)[\'\"]\\)");
    private static final Pattern resourceURLHTML    = Pattern.compile("(href|src|url)=[\'\"](((?!://).)*)[\'\"]");

    public static String makeCSSURLsAbsolute(String input, String baseURL)
    {
        Matcher matcher     = resourceURLCSS.matcher(input);
        return matcher.replaceAll("url('"+baseURL+"$1')");
    }
    public static String makeHTMLURLsAbsolute(String input, String baseURL)
    {
        Matcher matcher     = resourceURLHTML.matcher(input);
        return matcher.replaceAll("$1=\""+baseURL+"$2\"");
    }

    public static void main(String[] args)
    {
        String fixed    = RegexUtils.makeCSSURLsAbsolute("div#header { background-image: url('images/header-background.jpg'); } div#header { background-image: url('images/header-background.jpg'); }", "http://www.google.ca/");
        System.out.println(fixed);

        fixed           = RegexUtils.makeHTMLURLsAbsolute("href=\"wtfguys.css\" href=\"wtfguys.css\"", "http://www.google.ca/");
        System.out.println(fixed);
    }
}

К сожалению, этот код не делает того, что я ожидал, а именно замены всех вхождений регулярного выражения заменой строки. По сути, мы заменяем относительные URL-адреса в CSS и HTML абсолютными URL-адресами. Кажется, это только заменяет первое вхождение, давая

div#header { background-image: url('http://www.google.ca/images/header-background.jpg'); } div#header { background-image: url('images/header-background.jpg'); }
href="http://www.google.ca/wtfguys.css" href="wtfguys.css"

как вывод. Какие-либо предложения?


person gdoug    schedule 15.04.2016    source источник


Ответы (2)


Вы используете регулярные выражения, которые пытаются сопоставить значения атрибутов как в одинарных, так и в двойных кавычках. Дело в том, что вы можете сопоставить усеченное значение, потому что ни один из двух шаблонов не гарантирует, что открывающая кавычка соответствует закрывающей. Кроме того, эта цитата должна отсутствовать в самом значении.

Итак, заключите открывающую цитату в группу захвата, используйте обратную ссылку в качестве закрывающего разделителя и добавьте обратную ссылку в качестве альтернативы просмотру вперед в умеренном жадном токене. Затем исправьте шаблоны замены, так как порядок обратных ссылок изменится.

private static final Pattern resourceURLCSS     = Pattern.compile("url\\((['\"])((?:(?!://|\\1).)*)\\1\\)");
private static final Pattern resourceURLHTML    = Pattern.compile("(href|src|url)=(['\"])((?:(?!://|\\2).)*)\\2");

public static String makeCSSURLsAbsolute(String input, String baseURL)
{
    Matcher matcher     = resourceURLCSS.matcher(input);
    return matcher.replaceAll("url('"+baseURL+"$2')");
}
public static String makeHTMLURLsAbsolute(String input, String baseURL)
{
    Matcher matcher     = resourceURLHTML.matcher(input);
    return matcher.replaceAll("$1=\""+baseURL+"$3\"");
}

См. демонстрацию IDEONE.

person Wiktor Stribiżew    schedule 15.04.2016

.* жаден. Сопоставитель захватывает wtfguys.css" href="wtfguys.css как $2, а не wtfguys.css. Вместо этого вы можете использовать .*? или [^\"]*, поскольку внутри URL-адресов нет даже экранированных кавычек. Справочник по этой проблеме, объясняющий несколько вариантов (в том числе упомянутый Виктором): http://www.rexegg.com/regex-quantifiers.html#greedytrap.

person Alexey Romanov    schedule 15.04.2016
comment
И решение может заключаться в том, чтобы сделать его нежадным, добавив ? после *. - person totoro; 15.04.2016
comment
Нет, я думаю, что умеренные жадные токены должны быть исправлены, дело не только в ленивых или жадных квантификаторах. Иначе вокруг не было бы [\'\"]. Требуется еще одна группа захвата, обратная ссылка и альтернатива в негативном прогнозе. - person Wiktor Stribiżew; 15.04.2016
comment
Нет необходимости поддерживать экранированные последовательности, это не строки C. - person Wiktor Stribiżew; 15.04.2016