2009-10-08 28 views
24

Tôi luôn nghĩ rằng việc xác nhận trong regex-API của Java (và nhiều ngôn ngữ khác cho vấn đề đó) phải có độ dài hiển nhiên. Vì vậy, số lượng định lượng STAR và PLUS không được phép bên trong nhìn sau.Regex nhìn đằng sau mà không có độ dài tối đa rõ ràng trong Java

Các tuyệt vời trực tuyến tài nguyên regular-expressions.info dường như để xác nhận (một số) giả định của tôi:

"[...] Java có điều một bước xa hơn bằng cách cho phép lặp lại hữu hạn Bạn vẫn không thể sử dụng các ngôi sao. hoặc cộng, nhưng bạn có thể sử dụng dấu hỏi và dấu ngoặc nhọn với tham số max quy định. Java công nhận một thực tế rằng lặp lại hữu hạn có thể được viết lại như một thay đổi luân phiên các chuỗi với khác nhau, nhưng độ dài cố định. Thật không may, JDK 1.4 và 1.5 có một số lỗi khi bạn sử dụng thay đổi bên trong lookbehind. Những đã được sửa trong JDK 1.6. [...]"

- http://www.regular-expressions.info/lookaround.html

Sử dụng dấu ngoặc nhọn làm việc miễn là tổng chiều dài của hàng loạt các nhân vật bên trong nhìn đằng sau là nhỏ hơn hoặc bằng Integer.MAX_VALUE Vì vậy. những regexes là hợp lệ:

"(?<=a{0," +(Integer.MAX_VALUE) + "})B" 
"(?<=Ca{0," +(Integer.MAX_VALUE-1) + "})B" 
"(?<=CCa{0," +(Integer.MAX_VALUE-2) + "})B" 

Nhưng đây không phải là:

"(?<=Ca{0," +(Integer.MAX_VALUE) +"})B" 
"(?<=CCa{0," +(Integer.MAX_VALUE-1) +"})B" 

Tuy nhiên, tôi không hiểu thứ e sau:

Khi tôi chạy thử nghiệm bằng cách sử dụng * và + lượng hóa bên trong một nhìn đằng sau, mọi việc suôn sẻ (xem ra Test 1thử nghiệm 2).

Nhưng, khi tôi thêm một nhân vật duy nhất tại sự bắt đầu của nhìn đằng sau từ Test 1thử nghiệm 2, nó phá vỡ (xem ra thử nghiệm 3).

Làm tham lam * từ Kiểm tra 3 miễn cưỡng không có hiệu lực, nó vẫn bị ngắt (xem Kiểm tra 4).

Đây là khai thác thử nghiệm:

public class Main { 

    private static String testFind(String regex, String input) { 
     try { 
      boolean returned = java.util.regex.Pattern.compile(regex).matcher(input).find(); 
      return "testFind  : Valid -> regex = "+regex+", input = "+input+", returned = "+returned; 
     } catch(Exception e) { 
      return "testFind  : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    private static String testReplaceAll(String regex, String input) { 
     try { 
      String returned = input.replaceAll(regex, "FOO"); 
      return "testReplaceAll : Valid -> regex = "+regex+", input = "+input+", returned = "+returned; 
     } catch(Exception e) { 
      return "testReplaceAll : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    private static String testSplit(String regex, String input) { 
     try { 
      String[] returned = input.split(regex); 
      return "testSplit  : Valid -> regex = "+regex+", input = "+input+", returned = "+java.util.Arrays.toString(returned); 
     } catch(Exception e) { 
      return "testSplit  : Invalid -> "+regex+", "+e.getMessage(); 
     } 
    } 

    public static void main(String[] args) { 
     String[] regexes = {"(?<=a*)B", "(?<=a+)B", "(?<=Ca*)B", "(?<=Ca*?)B"}; 
     String input = "CaaaaaaaaaaaaaaaBaaaa"; 
     int test = 0; 
     for(String regex : regexes) { 
      test++; 
      System.out.println("********************** Test "+test+" **********************"); 
      System.out.println(" "+testFind(regex, input)); 
      System.out.println(" "+testReplaceAll(regex, input)); 
      System.out.println(" "+testSplit(regex, input)); 
      System.out.println(); 
     } 
    } 
} 

Sản lượng:

********************** Test 1 ********************** 
    testFind  : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true 
    testReplaceAll : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa 
    testSplit  : Valid -> regex = (?<=a*)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa] 

********************** Test 2 ********************** 
    testFind  : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = true 
    testReplaceAll : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = CaaaaaaaaaaaaaaaFOOaaaa 
    testSplit  : Valid -> regex = (?<=a+)B, input = CaaaaaaaaaaaaaaaBaaaa, returned = [Caaaaaaaaaaaaaaa, aaaa] 

********************** Test 3 ********************** 
    testFind  : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^
    testReplaceAll : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^
    testSplit  : Invalid -> (?<=Ca*)B, Look-behind group does not have an obvious maximum length near index 6 
(?<=Ca*)B 
    ^

********************** Test 4 ********************** 
    testFind  : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^
    testReplaceAll : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^
    testSplit  : Invalid -> (?<=Ca*?)B, Look-behind group does not have an obvious maximum length near index 7 
(?<=Ca*?)B 
    ^

Câu hỏi của tôi có thể rõ ràng, nhưng tôi vẫn sẽ hỏi nó: bất cứ ai có thể giải thích cho tôi tại sao Test 1 và không thành công và Kiểm tra 3 và không? Tôi đã mong đợi tất cả họ thất bại, không phải một nửa trong số họ làm việc và một nửa trong số họ thất bại.

Cảm ơn.

PS. Tôi đang sử dụng: Phiên bản Java 1.6.0_14

Trả lời

17

Lướt qua mã nguồn cho Pattern.java cho thấy rằng '*' và '+' được triển khai dưới dạng các thể hiện của Xoăn (là đối tượng được tạo cho các toán tử xoăn). Vì vậy,

a* 

được thực hiện như

a{0,0x7FFFFFFF} 

a+ 

được thực hiện như

a{1,0x7FFFFFFF} 

đó là lý do tại sao bạn thấy chính xác những hành vi tương tự cho curlies và các ngôi sao .

+0

Nó không xảy ra với tôi để xem xét nguồn gốc của mẫu ... Silly me. Cảm ơn bạn! Nó làm cho cảm giác hoàn hảo ngay bây giờ. –

+0

Một trong nhiều lý do tôi không thể sống mà không có Eclipse là ngón tay bấm ctrl ngứa của tôi (nếu bạn không sử dụng Eclipse, có nghĩa là "mở tệp nguồn nơi tên này được xác định"). –

+0

Cảm ơn, tôi chưa bao giờ gặp khó khăn để đính kèm nguồn vào Eclipse. Bây giờ tôi sẽ làm như vậy. Cảm ơn. –

13

Đó là một lỗi: http://bugs.sun.com/view_bug.do?bug_id=6695369

Pattern.compile() luôn phải ném một ngoại lệ nếu nó không thể xác định độ dài tối đa có thể của một trận đấu lookbehind.

+0

Xin chào Alan, bạn đã tìm ra sớm! Cảm ơn, tôi biết điều này từng là một lỗi (trong 1.5) nhưng tôi nhớ rằng động cơ luôn luôn được đánh giá là sai hoặc một cái gì đó. Bây giờ nó đánh giá kết quả một cách chính xác, vì vậy tôi đã tìm ra lỗi đã được sửa trong 1.6. Tôi chắc đã nhớ sai. Cảm ơn bạn về thông tin. Bart (còn gọi là prometheuzz) –

+0

Lỗi đó đã được sửa; đây là một cái mới được giới thiệu trong phiên bản 1.6. :/ –

+0

Ah, tuyệt vời: |. Cảm ơn bạn về thông tin. –

Các vấn đề liên quan