对于给定的输入字符串和给定的模式K,我想从字符串和中提取每个出现的K(或其某些部分(使用组)),检查整个字符串是否匹配K*
(因为它包含0个或更多K&没有其他字符)。
但我想使用正则表达式一次一次。更具体地说,我目前正在使用Matcher.find
找到该模式,但这并非严格要求。
我该怎么做?
我已经找到了一个解决方案(并发布了一个答案),但想知道是否有特定的正则表达式或Matcher
功能解决/可以解决这个问题,或者只是有更好/不同的方式正在做。但是,即使不是,我仍然认为这是一个有趣的问题。
示例:
模式:<[0-9]>
(<>
中的单个数字)
有效输入:<1><2><3>
输入无效:
<1><2>a<3>
<1><2>3
Oh look, a flying monkey!
<1><2><3
使用matches
进行2次传递的代码:
boolean products(String products)
{
String regex = "(<[0-9]>)";
Pattern pAll = Pattern.compile(regex + "*");
if (!pAll.matcher(products).matches())
return false;
Pattern p = Pattern.compile(regex);
Matcher matcher = p.matcher(products);
while (matcher.find())
System.out.println(matcher.group());
return true;
}
答案 0 :(得分:7)
由于当整个字符串与模式K*
不匹配时不清楚要输出什么,我将重新定义问题以明确在这种情况下输出什么。
给出任何模式K:
K*
。K*
,则将字符串拆分为与K
匹配的非重叠标记。K*
的前缀,则选择由K*+
1 选择的前缀,并将前缀拆分为与K匹配的标记。 1 我不知道是否有获得与K匹配的最长前缀。当然,您可以随时删除最后一个字符并对{{{ 1}}直到匹配,但显然效率低下。
除非另有说明,否则我在下面写的内容将遵循上面的问题描述。请注意,问题的第3个要点是解决要采用哪个前缀字符串的歧义。
如果我们能够解决问题,可以解决上述问题:
给定一个模式
K*
,这是一个重复捕获组,获取所有重复的捕获文本,而不是仅重复最后一次。
(K)*
,则通过与K*
匹配,我们可以获得与模式^(K)*$
匹配的所有标记。K
的前缀,则通过与K*
匹配,我们可以获得与模式^(K)*
匹配的所有令牌。.NET正则表达式就是这种情况,因为它保留了重复捕获组的所有捕获文本。
但是,由于我们使用的是Java,因此我们无法访问此类功能。
检查字符串是否具有模式K
始终可以使用K*
/ Matcher.matches()
来完成,因为引擎会对输入字符串进行全面的回溯以某种方式“统一”带有输入字符串的String.matches()
。困难的是将输入字符串拆分为与模式K*
匹配的标记。
K
等同于K*
如果模式K具有属性:
对于所有字符串 2 ,
K*+
等同于K*
,即输入字符串如何拆分为与模式K*+
匹配的标记是相同的
2 您可以仅为您正在操作的输入字符串定义此条件,但确保此前提条件并不容易。为所有字符串定义时,只需要分析正则表达式以检查条件是否成立。
然后可以构建解决问题的一次通过解决方案。您可以在模式K
上重复使用Matcher.find()
,并检查找到的最后一个匹配是否在字符串末尾。这与您当前的解决方案类似,不同之处在于您使用代码进行边界检查。
\GK
中量词+
之后的*
使量词占有。占有量词将阻止引擎回溯,这意味着每次重复始终是模式K的第一个可能匹配。我们需要此属性以使解K*+
具有相同的含义,因为它也将返回第一个可能的匹配对于模式K。
\GK
不等同于K*
如果没有上面的属性,我们需要2次传递才能解决问题。首先在模式K*+
上调用Matcher.matches()
/ String.matches()
。第二遍:
如果字符串与模式K*
不匹配,我们将在模式K*
上重复使用Matcher.find()
,直到找不到更多匹配项。这可以通过我们如何定义当输入字符串与模式\GK
不匹配时要采用的前缀字符串来完成。
如果字符串与模式K*
匹配,则在模式K*
上重复使用Matcher.find()
是一种解决方案。但是,这将导致与输入字符串的其余部分匹配的冗余工作。
请注意,此解决方案普遍适用于任何K.换句话说,它也适用于\GK(?=K*$)
等同于K*
的情况(但我们将使用更好的一次通过解决方案相反的情况。)
答案 1 :(得分:6)
这是对已经接受的答案的补充答案。下面是一个示例代码段,只使用m.find()
一次通过模式,这与您的一次通过解决方案类似,但不会解析不匹配的行。
import java.util.regex.*;
class test{
public static void main(String args[]){
String t = "<1><2><3>";
Pattern pat = Pattern.compile("(<\\d>)(?=(<\\d>)*$)(?<=^(<\\d>)*)");
Matcher m = pat.matcher(t);
while (m.find()) {
System.out.println("Matches!");
System.out.println(m.group());
}
}
}
正则表达式解释说:
<\\d>
- 这是你上面定义的k模式
?=
- 积极前瞻(检查K之前的内容)
<\\d>*
- 匹配k 0次或更多次
$
- 行结束
?<=
- 积极向后看(检查K背后的内容)
^
- 行的开头
<\\d>*
- 后跟0或更多Ks
正则表达式是美好的事物。
编辑:正如@nhahtdh所指出的那样,这只是答案的实现版本。事实上,上述实施可以通过答案中的知识得到改善。
(<\\d>)(?=(<\\d>)*$)(?<=^(<\\d>)*)
可以更改为\\G<\\d>(?=(<\\d>)*$)
。
答案 2 :(得分:3)
以下是使用Matcher.start
和Matcher.end
的一次性解决方案。
boolean products(String products)
{
String regex = "<[0-9]>";
Pattern p = Pattern.compile(regex);
Matcher matcher = p.matcher(products);
int lastEnd = 0;
while (matcher.find())
{
if (lastEnd != matcher.start())
return false;
System.out.println(matcher.group());
lastEnd = matcher.end();
}
if (lastEnd != products.length())
return false;
return true;
}
唯一的缺点是它会在找到无效数据之前打印(或处理)所有值。
例如,products("<1><2>a<3>");
将打印出来:
<1>
<2>
抛出异常之前的(因为直到字符串有效为止)。
要么发生这种情况,要么暂时存放所有这些似乎都是不可避免的。
答案 3 :(得分:1)
String t = "<1><2><3>";
Pattern pat = Pattern.compile("(<\\d>)*");
Matcher m = pat.matcher(t);
if (m.matches()) {
//String[] tt = t.split("(?<=>)"); // Look behind on '>'
String[] tt = t.split("(?<=(<\\d>))"); // Look behind on K
}