说我有以下正则表达式; (我正在使用java.util.regex包.java版本1.7.0_21)
Pattern p = Pattern.compile("\\G[^,]*(,|$)");
重复调用find(),我应该能够提取CSV中的字段,如下所示:
String myCSV = "a,b";
所以让我们用最简单的循环来尝试。只需回显每个匹配的信息。
Matcher m = p.matcher(myCSV);
while (m.find()) {
System.out.println("Match found from: " + m.start()
+ " (included) to: " + m.end()+ " (excluded),"
+ " matching: '" + m.group() + "'. Does it hit end?" + m.hitEnd());
}
虽然我试图构建我的正则表达式,以便它不允许零长度匹配,但非常令人惊讶的是,它确实:
Match found from: 0 (included) to: 2 (excluded), matching: 'a,'. Does it hit end?false
Match found from: 2 (included) to: 3 (excluded), matching: 'b'. Does it hit end?true
Match found from: 3 (included) to: 3 (excluded), matching: ''. Does it hit end?true
看看第3次匹配,在我的想法中不应该出现。的确,我的正则表达式要求每个匹配以(,| $)结尾。因此,第二个匹配,需要达到并“消耗”字符串的结尾才有效:它不会让它进一步匹配!
在第二场比赛之后,这似乎被hitEnd确认了! -
但是这似乎不被find内部状态所考虑,它会搜索进一步的匹配,并且此时它显然会找到它,因为正则表达式允许零长度匹配,然后结束字符串因为它是一个有效的匹配,因为每个字段都允许是一个空字符串(如果不是这种情况,使用+代替*显然会解决问题)。
我问两件事
1)解决这个问题的方法
2)它似乎与字符串的末尾匹配两次的原因
答案 0 :(得分:1)
您的第一个问题有几个可能的答案。一种是使用lookbehind来确保你总是在行开头或逗号后开始匹配,如:
(?<=^|,)([^,]*)(?:,|$)
如http://rubular.com/r/L5d8lZ44kh
所示出于演示目的,我为非分隔符匹配引入了捕获组,并使用非捕获组进行分隔符匹配。在您的情况下,这些变化都不是必需的;你只需要确保包括后观变化。
另外,正如@sin在评论中指出的那样,没有必要匹配上一场比赛的结束,所以我淘汰了\ G.如果您将可接受的CSV“值”字符限制为比逗号以外的所有字符更窄的集合,则情况并非如此。
第二个问题的答案有点棘手。首先,重要的是要理解锚点位置(例如,行的开头,最后一个匹配的结束,行尾等)永远不会被正则表达式捕获;他们只是匹配。比赛位置保持不变。因此,多个连续的表达式可以匹配相同的锚点,就像您所经历的那样。
除了需要有一种方法可以避免匹配无数个零长度表达式之外,这个工作正常。至少有几种方法可以实现这一点,如http://www.regular-expressions.info/zerolength.html中所述。虽然这对你的问题并不是非常关键,但我尝试通过谷歌搜索来确定Java实现使用哪种方法,但不能。
答案 1 :(得分:0)
我不确定原因,也许它会从每个可能的起点(即b
以及字符串的末尾)寻找匹配?
要解决此问题,您可以在正则表达式的开头添加另一个部分,以查找字符串的开头或逗号。
类似于:"(,|^)[^,]*(,|$)"
但是你需要从比赛中删除额外的逗号,或许是在捕捉一个小组而不是整场比赛?
E.g。 "(,|^)([^,]*)(,|$)"
,然后使用m.group(2)
答案 2 :(得分:0)
似乎简单的解决方案是将正则表达式分为两部分
,
之后就可以接受空字符串。non-comma
字符串的其他字符串这似乎可以解决问题
Pattern p = Pattern.compile("\\G[^,]*,|\\G[^,]+$");
String myCSV = "a,,b";
Matcher m = p.matcher(myCSV);
while (m.find()) {
System.out.println("Match found from: " + m.start()
+ " (included) to: " + m.end() + " (excluded),"
+ " matching: '" + m.group() + "'. Does it hit end?"
+ m.hitEnd());
}
输出:
Match found from: 0 (included) to: 2 (excluded), matching: 'a,'. Does it hit end?false
Match found from: 2 (included) to: 3 (excluded), matching: ','. Does it hit end?false
Match found from: 3 (included) to: 4 (excluded), matching: 'b'. Does it hit end?true
其他甚至更简单的方法就是在每个逗号上使用split
。如果你想要最后一个空字符串,你可以使用分割与负限制,如
for(String token:"a,,b,".split(",",-1)){
System.out.println("'"+token+"'");
}
此外,如果你想在逗号中加入逗号,你可以使用look-behind机制在每个逗号后分割
for(String token:"a,,b,".split("(?<=,)",-1)){
System.out.println("'"+token+"'");
}
答案 3 :(得分:0)
Pattern p = Pattern.compile("[^,]+(?=\\s*|\\s*$)");
参见演示 here 。
答案 4 :(得分:0)
可能是正则表达式解决方案 -
# "(?:^|(?<=,))([^,]*)(?:,|$)"
(?:
^
| (?<= , )
)
( [^,]* ) # (1)
(?: , | $ )
答案 5 :(得分:0)
如果您不想在值后显示,
作为匹配项的一部分,则您可以在开头时匹配(^|,)
,而不是最后匹配(,|$)
,这将消除你的问题:
\G(^|,)[^,]*
RegexHero shows 2 matches代替3 matches
如果您在同一个字符串中使用多行,则将行分隔符添加到否定的类中。