为什么这个先行断言不能用Java工作?

时间:2011-07-02 15:18:30

标签: java regex lookahead

我来自Perl背景,我习惯做类似以下的事情来匹配字符串中的前导数字并执行就地增量:

my $string = '0_Beginning';

$string =~ s|^(\d+)(?=_.*)|$1+1|e;

print $string;        # '1_Beginning'

由于我对Java的了解有限,事情并不那么简洁:

String string = "0_Beginning";

Pattern p = Pattern.compile( "^(\\d+)(?=_.*)" );

String digit = string.replaceFirst( p.toString(), "$1" ); // To get the digit

Integer oneMore = Integer.parseInt( digit ) + 1;          // Evaluate ++digit

string.replaceFirst( p.toString(), oneMore.toString() );  //

正则表达式在这里不匹配......但它在Perl中完成。

我在这里做错了什么?

2 个答案:

答案 0 :(得分:2)

实际上它匹配。你可以通过打印找到

System.out.println(p.matcher(string).find());

问题在于行

String digit = string.replaceFirst( p.toString(), "$1" );

这实际上是无用的,因为它将第一组替换为第一组(前提不是匹配的一部分)与第一组的内容。

您可以通过以下代码获得所需的结果(即数字)

Matcher m = p.matcher(string);
String digit = m.find() ? m.group(1) : "";

注意:如果没有匹配,您应该检查m.find()。在这种情况下,您可能无法拨打parseInt,但您会收到错误消息。因此,完整的代码看起来像

Pattern p = Pattern.compile("^(\\d+)(?=_.*)");

String string = "0_Beginning";

Matcher m = p.matcher(string);
if (m.find()) {
    String digit = m.group(1);
    Integer oneMore = Integer.parseInt(digit) + 1;
    string = m.replaceAll(oneMore.toString());
    System.out.println(string);
} else {
    System.out.println("No match");
}

答案 1 :(得分:2)

让我们看看你在这做什么。

String string = "0_Beginning";
Pattern p = Pattern.compile( "^(\\d+)(?=_.*)" );

声明并初始化String和pattern对象。

String digit = string.replaceFirst( p.toString(), "$1" ); // To get the digit

(您正在将模式转换回字符串,并且replaceFirst从此创建一个新模式。这是故意的吗?)

正如Howard所说,这会将字符串中模式的第一个匹配替换为第一个组的内容,而模式的匹配仅为0,作为第一个组。因此digit等于string,...

Integer oneMore = Integer.parseInt( digit ) + 1;          // Evaluate ++digit

...你的解析失败了。

string.replaceFirst( p.toString(), oneMore.toString() );  //

这可行(但将模式再次转换为字符串并返回模式)。

我将如何做到这一点:

String string = "0_Beginning";
Pattern p = Pattern.compile( "^(\\d+)(?=_.*)" );

Matcher matcher = p.matcher(string);
StringBuffer result = new StringBuffer();
while(matcher.find()) {
    int number = Integer.parseInt(matcher.group());
    m.appendReplacement(result, String.valueOf(number + 1));
}
m.appendTail(result);
return result.toString(); // 1_Beginning

(当然,对于你的正则表达式,循环只执行一次,因为正则表达式是锚定的。)


编辑:澄清关于string.replaceFirst的声明:

此方法不返回模式,但在内部使用一个模式。 From the documentation

  

将与给定正则表达式匹配的此字符串的第一个子字符串替换为给定的替换。

     

调用str.replaceFirst(regex, repl)形式的此方法会产生与表达式

完全相同的结果
Pattern.compile(regex).matcher(str).replaceFirst(repl)

在这里,我们看到从第一个参数编译新模式。

这也向我们展示了你想要做的事情的另一种方式:

String string = "0_Beginning";
Pattern p = Pattern.compile( "^(\\d+)(?=_.*)" );
Matcher m = p.matcher(string);
if(m.find()) {
    digit = m.group();
    int oneMore = Integer.parseInt( digit ) + 1
    return m.replaceFirst(string, String.valueOf(oneMore));
}

这只会编译一次模式,而不是像原始程序一样编译三次 - 但仍会进行两次匹配(一次用于查找,一次用于replaceFirst),而不是像我的程序一样。