说我有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范围因为start
和end
将保持相同的值。
我怎样才能克服这一点? regex
是唯一的解决方案吗?
答案 0 :(得分:2)
正则表达式是正确的工具。使用Pattern
和Matcher
。
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
确保open
和close
的所有特殊字符都被视为常规字符。
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
。这会使我们第一次看到filtered
:filtered
的行。如果您修复该行,您的解决方案就可以正常运行。
<强>迭代强>:
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);
}
}
}
}
不确定这会如何变得更简单或更清洁。绝对清楚代码在做什么。