使用正则表达式进行条件替换

时间:2009-06-10 14:14:17

标签: java regex replace

当谈到正则表达式时,我是一个相对新手,但我开始掌握它。我开始在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?

在网上搜索了一段时间后,我终于能够将不同表达式的部分拼凑在一起,帮助我做我正在寻找的事情(一些例子包括在实际网址的网址末尾的尾随句点,一些编码已经在锚标签中的网址等。)

这是我到目前为止所做的:

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 - 它只调用了LinksifiedString(s,null)

就像我说的那样,这种模式正在捕捉我需要捕获的所有内容,而且除了链接以www开头的情况外,replaceAll对每种情况都很有效。如果匹配以“www”开头而不是像“http”或“ftp”这样的协议,我想在结果链接前有条件地添加“http://”。那就是:

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

应该返回

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

匹配组如下:

  • $ 0 - 找到的实际网址: 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 个答案:

答案 0 :(得分:10)

对于你的具体问题,肯定会采用Tomalak所说的回调函数。

对于所有那些斜线的问题,以及各种其他奇怪的事情......

以下是您当前的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+)+。 (那是Java字符串中的(\\w+?\\.\\w+)+。)

第四行 - 哇,逃脱了很多!!几乎都是不必要的。这样做:([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~!@#$%^&*()_\\-=+\\/?.:;',]*)?([^.'# !])

但是我坚持使用多线 - 只是plonk (?x)一开始它是一个有效的正则表达式,它忽略了空格,你可以使用#s进行评论 - 对于正则表达式总是一件好事只要这个!

答案 1 :(得分:4)

看起来您需要一个回调函数,它返回一个动态结果,而不是您当前在replaceAll()中使用的固定字符串。

我想你可以从这个问题的接受答案中做出一些事情:Java equivalent to PHP's preg_replace_callback