Подтвердить что ты не робот

Ошибка в реализации регулярного выражения Java?

Я обнаружил некоторое неожиданное поведение в реализации Java regex. При использовании java.util.regex.Pattern и java.util.regex.Matcher следующее регулярное выражение не соответствует корректно на входе "Merlot" при использовании метода Matcher find():

((?:White )?Zinfandel|Merlot)

Если я изменяю порядок выражений внутри самой внешней сопоставимой группы, метод Matcher find() соответствует.

(Merlot|(?:White )?Zinfandel)

Вот несколько тестовых кодов, которые иллюстрируют проблему.

RegexTest.java

import java.util.regex.*;

public class RegexTest {
    public static void main(String[] args) {
        Pattern pattern1 = Pattern.compile("((?:White )?Zinfandel|Merlot)");
        Matcher matcher1 = pattern1.matcher("Merlot");
        // prints "No Match :("
        if (matcher1.find()) {
            System.out.println(matcher1.group(0));
        } else {
            System.out.println("No match :(");
        }

        Pattern pattern2 = Pattern.compile("(Merlot|(?:White )?Zinfandel)");
        Matcher matcher2 = pattern2.matcher("Merlot");
        // prints "Merlot"
        if (matcher2.find()) {
            System.out.println(matcher2.group(0));
        } else {
            System.out.println("No match :(");
        }
    }
}

Ожидаемый результат:

Merlot
Merlot

Но фактический вывод:

No Match :(
Merlot

Я проверил, что это неожиданное поведение существует в Java версии 1.7.0_11 на Ubuntu linux, а также Java версии 1.6.0_37 на OSX 10.8.2. Вчера я сообщил об этом поведении в качестве ошибки Oracle и получил автоматическое письмо, в котором сообщается, что мой отчет об ошибке получен и имеет внутренний идентификатор проверки 2441589. Я могу" t найти отчет об ошибке, когда я ищу этот идентификатор в своей базе данных ошибок. (Вы слышите сверчков?)

Я обнаружил ошибку в Java, предположительно тщательно протестированную и использующую реализацию регулярного выражения (трудно поверить в 2013), или я делаю что-то неправильно?

4b9b3361

Ответ 1

Кажется, что исправлено в Java 1.8.

Welcome to Scala version 2.11.0-20130930-063927-2bba779702 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0-ea).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import java.util.regex._
import java.util.regex._

scala> Pattern.compile("((?:White )?Zinfandel|Merlot)")
res0: java.util.regex.Pattern = ((?:White )?Zinfandel|Merlot)

scala> .matcher("Merlot")
res1: java.util.regex.Matcher = java.util.regex.Matcher[pattern=((?:White )?Zinfandel|Merlot) region=0,6 lastmatch=]

scala> .find()
res2: Boolean = true

Ответ 2

Следующее:

import java.util.regex.*;

public class T {
  public static void main( String args[] ) {
    System.out.println( Pattern.compile("(a)?bb|c").matcher("c").find() );
    System.out.println( Pattern.compile("(a)?b|c").matcher("c").find() );
  }
}

печатает

false
true

на

  • JDK 1.7.0_13
  • JDK 1.6.0_24

Следующее:

import java.util.regex.*;

public class T {
  public static void main( String args[] ) {
    System.out.println( Pattern.compile("((a)?bb)|c").matcher("c").find() );
    System.out.println( Pattern.compile("((a)?b)|c").matcher("c").find() );
  }
}

печатает:

true
true

Ответ 3

Я не понимаю все, что происходит, но я играл с вашим примером, чтобы попытаться извлечь некоторую диагностическую информацию, которую вы могли бы добавить в отчет об ошибке.

Во-первых, если вы используете притяжательный квантификатор, он работает, но я не знаю, почему:

Pattern pattern1 = Pattern.compile("((?:White )?+Zinfandel|Merlot)");

Кроме того, если первая группа в выборе короче второй, то она работает в любом случае:

Pattern pattern1 = Pattern.compile("((?:White )?Zinf|Merlot)");

Как я уже сказал, я действительно не понимаю, как это может быть. Ни один из этих двух находок не имеет для меня никакого смысла, а просто думал, что я поделюсь...

Ответ 4

Ошибка была явно исправлена ​​в Java 8, и была решена "Не исправлена ​​" как backport для Java 7. Однако в качестве обходного пути вы можете либо использовать независимую (атомную) группировку для "белого", либо вы можете изолировать тестовый пример для "Белый Zinfandel", обернув его в отдельную переменную тестовую группу.

В вашем примере в группе захвата указано следующее.

Модификатор группы без фиксации (?:White)

((?:White )?Zinfandel|Merlot)

Как работа с использованием независимой группы захвата будет успешной.

Независимый не-захватывающий групповой модификатор (?>White)

((?>White )?Zinfandel|Merlot)

Повторное создание тестового примера для независимой группы, не связанной с захватом или чередованием групп в Java 1.7.0_71, работает.

java version "1.7.0_71"
Java(TM) SE Runtime Environment (build 1.7.0_71-b14)
Java HotSpot(TM) 64-Bit Server VM (build 24.71-b01, mixed mode)

Независимая группа без группировки или групповая чередование

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class RegexTest {

    public static void main( String[] args ) {

        Pattern independentNCG = Pattern.compile( "((?>White )?Zinfandel|Merlot)" );
        Matcher independentNCGMatcher = independentNCG.matcher( "Merlot" );

        Pattern alternateGroupPattern = Pattern.compile( "(((?:White )?Zinfandel)|Merlot)" );
        Matcher alternateGroupMatcher = alternateGroupPattern.matcher( "Merlot" );

        System.out.println( independentNCGMatcher.find() ? independentNCGMatcher.group( 0 ) : "No match found for Merlot" );
        System.out.println( alternateGroupMatcher.find() ? alternateGroupMatcher.group( 0 ) : "No match found for Merlot" );

    }
}

ВОЗВРАТ

Merlot
Merlot