Условная замена на регулярное выражение - PullRequest
2 голосов
/ 10 июня 2009

Я относительный новичок, когда дело доходит до регулярных выражений, но я начинаю понимать это. Я начал писать метод в Java для «связывания» строки, то есть сканирования его на наличие ссылок на URL-адреса (например, «http: // ...») или строк, которые выглядят как веб-адреса ( "www.example.com ...")

Так, например, если бы у меня была строка, которая выглядела так:

My favorite site is http://www.example.com.  What is yours?

Пройдя через метод, вы получите строку с надписью:

My favorite site is <a href="http://www.example.com">http://www.example.com</a>.  What is yours?

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

Вот что у меня есть:

public static String toLinkifiedString(String s, IAnchorBuilder anchorBuilder)
{
    if (IsNullOrEmpty(s))
    {
        return Empty;
    }

    String r = "(?<![=\"\"\\/>])(www\\.|(http|https|ftp|news|file)(s)?://)([\\w+?\\.\\w+])+([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?([^.|'|# |!])";

    Pattern pattern = Pattern.compile(r, Pattern.DOTALL | Pattern.UNIX_LINES | Pattern.CASE_INSENSITIVE);
    Matcher matcher = pattern.matcher(s);
    if (anchorBuilder != null)
    {
        return matcher.replaceAll(anchorBuilder.createAnchorFromUrl("$0"));
    }
    return matcher.replaceAll("<a href=\"$0\">$0</a>"); // group 0 is the whole expression
}

public interface IAnchorBuilder
{
    public String createAnchorFromUrl(String url);
}

Существует также простой вариант toLinkifiedString , который принимает только строку s - он просто вызывает toLinkifiedString (s, null)

Итак, как я уже сказал, этот шаблон перехватывает все, что мне нужно, чтобы перехватить, и replaceAll отлично работает для всех случаев, кроме случаев, когда ссылка начинается с www. Если совпадение начинается с «www» вместо протокола, например «http» или «ftp», я хочу условно добавить «http://" перед полученной ссылкой. То есть:

MyClass.toLinkifiedString("go to www.example.org") 

должен вернуть

go to <a href="http://www.example.com">www.example.org</a>

Подходящие группы:

  • $ 0 - фактический найденный URL: http://www.example.org или www.example.net
  • $ 1 - совпадение протокола ("http://" или" www "для ссылок без протоколов)

Полагаю, что я хочу, чтобы в псевдокоде было что-то вроде:

matcher.replaceAll("<a href="(if protocol = "www", insert "http://" + url - otherwise, insert url">url</a>"

Возможно ли это? Или я должен быть доволен возможностью создания якорей только из ссылок, начинающихся с "http: // ...":)

Спасибо за любую помощь, которую может предложить каждый

Ответы [ 2 ]

10 голосов
/ 10 июня 2009

Для вашей конкретной проблемы определенно используйте функцию обратного вызова, как говорит Томалак.

За проблему всех этих косых черт и множества других странностей ...

Вот ваше текущее Java-регулярное выражение, разбитое на строки:

(?<![=\"\"\\/>])
(www\\.|(http|https|ftp|news|file)(s)?://)
([\\w+?\\.\\w+])+
([a-zA-Z0-9\\~\\!\\@\\#\\$\\%\\^\\&amp;\\*\\(\\)_\\-\\=\\+\\\\\\/\\?\\.\\:\\;\\'\\,]*)?
([^.|'|# |!])

И то же самое, что и регулярное выражение, не относящееся к Java (строка Java не экранируется):

(?<![=""\/>])
(www\.|(http|https|ftp|news|file)(s)?://)
([\w+?\.\w+])+
([a-zA-Z0-9\~\!\@\#\$\%\^\&amp;\*\(\)_\-\=\+\\\/\?\.\:\;\'\,]*)?
([^.|'|# |!])


А вот описание того, что с ним не так ...:)

Первая строка - вы дублируете " в классе символов и вам не нужно экранировать /

Вторая строка - хорошо, за исключением того, что я не уверен, что вы ищете с частью (s)?, так как у вас все равно есть https в предыдущей группе.

Строка третья - вы знаете, что у вас есть класс персонажей? квантификаторы не работают. Вы, вероятно, хотите вместо (\w+?\.\w+)+. (Это (\\w+?\\.\\w+)+ в строке Java.)

Строка четвертая - Ух ты, сколько побега !! Почти все ненужное. Попробуйте: ([a-zA-Z0-9~!@#$%^&*()_\-=+\/?.:;',]*)? (и снова: ([a-zA-Z0-9~!@#$%^&*()_\\-=+\\/?.:;',]*)?)

Строка пять - чередование внутри класса символов ничего не делает. Это будет делать: [^.'#!], и добавить один |, если вы действительно хотите запретить использование символов канала.

Объединение всех этих комментариев дает следующее регулярное выражение:

(?<![="/>])
(www\.|(http|https|ftp|news|file)://)
(\w+?\.\w+)+
([a-zA-Z0-9~!@#$%^&*()_\-=+\/?.:;',]*)?
([^.'# !])

Или, еще раз, с экранированием для Java:

(?<![=\"/>])
(www\\.|(http|https|ftp|news|file)://)
(\\w+?\\.\\w+)+
([a-zA-Z0-9~!@#$%^&*()_\\-=+\\/?.:;',]*)?
([^.'# !])

Обратите внимание, насколько это проще!

Возвращаясь к единственной строке, это дает:

(?<![="/>])(www\.|(http|https|ftp|news|file)://)(\w+?\.\w+)+([a-zA-Z0-9~!@#$%^&*()_\-=+\/?.:;',]*)?([^.'# !])

или

(?<![=\"/>])(www\\.|(http|https|ftp|news|file)://)(\\w+?\\.\\w+)+([a-zA-Z0-9~!@#$%^&*()_\\-=+\\/?.:;',]*)?([^.'# !])

Но я бы придерживался многострочного - просто вставьте (?x) в самом начале, и это действительное регулярное выражение, которое игнорирует пробел, и вы можете использовать # для комментирования - всегда хорошо с регулярными выражениями. как это!

4 голосов
/ 10 июня 2009

Похоже, вам нужна функция обратного вызова, которая возвращает динамический результат, который вы можете использовать вместо фиксированной строки, которая у вас есть в replaceAll().

Я полагаю, что вы можете сделать что-то из принятого ответа на этот вопрос: Java эквивалентно PHP preg_replace_callback.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...