查找以分隔符包装的String部分

时间:2013-12-03 02:31:19

标签: java regex recursion

说我有String这样:

String s="social network such as '''[http://www.facebook.com Facebook]''' , "+
"'''[http://www.twitter.com Twitter]''' and '''[http://www.tumblr.com tumblr]'''";

我需要只检索Strings'''[中的]'''

示例输出:

http://www.facebook.com Facebook, http://www.twitter.com Twitter, http://www.tumblr.com   tumblr

我使用regex执行此操作时遇到了困难,因此我使用recursion提出了这个想法:

System.out.println(filter(s, "'''[",  "]'''"));
....

public static String filter(String s, String open, String close){   
  int start = s.indexOf(open);
  int end = s.indexOf(close);

  filtered = filtered + s.substring(start + open.length(), end) + ", ";
  s = s.substring(end + close.length(), s.length());

  if(s.indexOf(open) >= 0 && s.indexOf(close) >= 0)
     return filter(s, open, close);

  else
     return filtered.substring(0, filtered.length() - 2);
}

但在某些情况下,我需要在String的同一模式中检索单词,例如在''''''内,它会说 String index out of范围因为startend将保持相同的值。

我怎样才能克服这一点? regex是唯一的解决方案吗?

3 个答案:

答案 0 :(得分:2)

正则表达式是正确的工具。使用PatternMatcher

public static String filter(String s, String open, String close){
    Pattern p = Pattern.compile(Pattern.quote(open) + "(.*?)" + Pattern.quote(close));
    Matcher m = p.matcher(s);

    StringBuilder filtered = new StringBuilder();

    while (m.find()){
        filtered.append(m.group(1)).append(", ");
    }
    return filtered.substring(0, filtered.length() - 2); //-2 because trailing ", "
}

Pattern.quote确保openclose的所有特殊字符都被视为常规字符。

m.group()返回与String匹配的最后m.find()组。

m.find()查找与正则表达式匹配的所有子字符串。


非正则表达式解决方案:

注意:在这两个方面,end使用String#indexOf(String, int)StringBuilder#indexOf(String, int)分配了s.indexOf(close, start + 1),即使open和{{1}也是如此}值相同,不会发生错误。

<强>递归

close

不是直接添加public static String filter(String s, String open, String close){ int start = s.indexOf(open); int end = s.indexOf(close, start + 1); //I took the liberty of adding "String" and renaming your variable String get = s.substring(start + open.length(), end); s = s.substring(end + close.length()); if (s.indexOf(open) == -1){ return get; } return get + ", " + filter(s, open, close); } ,而是稍后处理它会更容易一些。另请注意,", "s.substring(end + close.length(), s.length())相同。另外,我觉得查看s.substring(end + close.length());而不是检查s.indexOf(...) == -1是否更为简洁。

真正的问题在于你对待>=0的方式。首先,您需要将filtered声明为filtered类型。接下来,由于您正在进行递归,因此不应该连接到String。这会使我们第一次看到filteredfiltered的行。如果您修复该行,您的解决方案就可以正常运行。

<强>迭代

String filtered = s.substring(start + open.length(), end) + ", ";

这种迭代方法使用public static String filter(String str, String open, String close){ int open_length = open.length(); int close_length = close.length(); StringBuilder s = new StringBuilder(str); StringBuilder filtered = new StringBuilder(); for (int start = s.indexOf(open), end = s.indexOf(close, start + 1); start != -1; start = s.indexOf(open), end = s.indexOf(close, start + 1)){ filtered.append(s.substring(start + open_length, end)).append(", "); s.delete(0, end + close_length); } return filtered.substring(0, filtered.length() - 2); //trailing ", " } ,但没有它可以做同样的事情。它产生两个StringBuilder个,一个空的,一个保存原始StringBuilder的值。在String循环中:

  • for获取对索引的引用
  • 如果int start = s.indexOf(open), end = s.indexOf(close)不包含start != -1 ,则
  • s结束循环 在循环的每次迭代之后
  • open,再次找到索引。

循环内部将正确的子字符串附加到start = s.indexOf(open), end = s.indexOf(close),并从另一个finished中删除附加的部分。

答案 1 :(得分:2)

别介意其他答案中的所有代码......您可以在一行中完成:

String[] urls = str.replaceAll("^.*?'''\\[|\\]'''(?!.*\\]''').*", "").split("\\]'''.*?'''\\[");

首先剥离前导和尾随的jetsam,然后在分隔符上分割,该分隔符匹配目标之间的所有内容。


这可以适用于具有可变分隔符的灵活解决方案:

public static String[] extract(String str, String open, String close) {
    return str.replaceAll("^.*?(\\Q" + open + "\\E|$)|\\Q" + close + "\\E(?!.*\\Q" + close + "\\E).*", "").split("\\Q" + close + "\\E.*?\\Q" + open + "\\E");
}

此正则表达式还通过返回具有单个空白元素的数组来满足没有目标。

P.S。这是我第一次使用引用语法\Q...\E来回想起正则表达式中的字符作为文字,所以我对此感到非常满意。

我还想声称在我的iPhone上输入整个东西的一些吹牛的权利(注意这意味着可能有一两个字符不合适,但它应该非常接近)。

答案 2 :(得分:0)

您可以非常轻松地使用字符串标记器。只需将整个字符串传递给tokenizer,然后询问每个令牌并检查它是否以您的分隔符开头。如果是,请将内容提取到结果集中。

字符串标记器版本将不那么高,而不像摄政方案那样难看。

以下是tokenizer版本:

public class TokenizerTest {

    @Test
    public void canExtractNamesFromTokens(){
        String openDelimiter = "'''[";
        String closeDelimiter = "]'''";
        String s="social network such as '''[http://www.facebook.com Facebook]''' , "+
            "'''[http://www.twitter.com Twitter]''' and '''[http://www.tumblr.com tumblr]'''";

        StringTokenizer t = new StringTokenizer(s);

        while (t.hasMoreElements()){
            String token = t.nextToken();
            if (token.startsWith(openDelimiter)){
                String url = token.substring(openDelimiter.length());
                token = t.nextToken();
                String siteName = token.substring(0, token.length()-closeDelimiter.length());
                System.out.println(url + " " + siteName);
            }
        }
   }
}

不确定这会如何变得更简单或更清洁。绝对清楚代码在做什么。