Câu hỏi Tại sao \ R hoạt động khác trong các biểu thức chính quy giữa Java 8 và Java 9?


Mã sau đây biên dịch trong cả Java 8 & 9, nhưng hoạt động khác nhau.

class Simple {
    static String sample = "\nEn un lugar\r\nde la Mancha\nde cuyo nombre\r\nno quiero acordarme";

    public static void main(String args[]){
        String[] chunks = sample.split("\\R\\R");
        for (String chunk: chunks) {
            System.out.println("Chunk : "+chunk);
        }
    }
}

Khi tôi chạy nó với Java 8, nó trả về:

Chunk : 
En un lugar
de la Mancha
de cuyo nombre
no quiero acordarme

Nhưng khi tôi chạy nó với Java 9 đầu ra là khác nhau:

Chunk : 
En un lugar
Chunk : de la Mancha
de cuyo nombre
Chunk : no quiero acordarme

Tại sao?


76
2017-12-18 15:53


gốc


Có vẻ như trong Java 8 \R là tham lam, trong khi trong 9 nó không phải là. - doublep
Bạn nhận được chuỗi nào từ System.getProperty("line.separator")? - dasblinkenlight
@ dasblinkenlight: Điều đó không quan trọng; \R Là người so khớp dòng. Nó sẽ phù hợp với bất cứ điều gì OP có ở đó. - Makoto
Khi đăng loại câu hỏi này, nó đáng giá bao gồm cả số phiên bản JDK bởi vì đôi khi đây là lỗi cố định trong bản phát hành điểm và sau đó mọi người không thể sao chép v.v. - ArtB
@ doublep Tôi không chắc chắn bạn sẽ gọi nó tham lam, nhưng nó không được phép quay lại và phá vỡ một chuỗi CR LF duy nhất trong hai khi phù hợp \R, bởi vì đó là bị cấm từ kết hợp chỉ là một CR nếu có LF sau. Một cách khác để diễn đạt điều này là nó không thể quay lại. Java 8 là chính xác; Java 9 bây giờ là không phù hợp với tr18 như xa như tôi có thể phân biệt. - tchrist


Các câu trả lời:


Các Tài liệu Java không phù hợp với Tiêu chuẩn Unicode. Javadoc làm sai những gì \R được cho là phù hợp. Nó đọc:

\R   Bất kỳ chuỗi dòng mã Unicode nào, tương đương với \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

Tài liệu Java đó là lỗi. Trong nó phần trên các ngắt dòng R1.6, tiêu chuẩn kỹ thuật Unicode # 18 về biểu thức chính quy rõ ràng:

Chúng tôi khuyên bạn nên có ký tự meta biểu thức chính quy, chẳng hạn như "\ R", để khớp tất cả các ký tự kết thúc dòng và chuỗi được liệt kê ở trên (ví dụ: trong # 1). Điều này sẽ tương ứng với một cái gì đó tương đương với biểu thức sau đây. Biểu thức đó hơi phức tạp do cần phải tránh sao lưu.

 (?:\u{D A}|(?!\u{D A})[\u{A}-\u{D}\u{85}\u{2028}\u{2029}]

Nói cách khác, nó chỉ có thể khớp với hai chuỗi CR + LF (vận chuyển trả về + dòng cấp dữ liệu) hoặc cái gì đó khác một điểm mã duy nhất từ ​​tập hợp đó với điều kiện là không phải chỉ là một chiếc xe ngựa trở về một mình, sau đó được theo dõi bởi một linefeed. Đó là bởi vì nó là không được phép sao lưu. CRLF phải là nguyên tử cho \R để hoạt động đúng.

Vì vậy, Java 9 không còn phù hợp với những gì R1.6 đề xuất mạnh mẽ. Hơn nữa, nó hiện đang làm một cái gì đó mà nó được cho là KHÔNG làm, và không làm, trong Java 8.

Có vẻ như đã đến lúc tôi cho Sherman (đọc: Xueming Shen) một lần nữa. Tôi đã làm việc với anh ta trước đây về những vấn đề nặng nề về sự tuân thủ chính thức này.


46
2017-12-19 02:28



Vì vậy, một cách giải quyết khác là sử dụng (?>\\R) hoặc là \\R{1}+ thay vì \\Rhoặc trong trường hợp cụ thể của OP, hãy sử dụng \\R{2}+ thay vì \\R\\R. Thật thú vị, thậm chí \\R{1}\\R{1} hoặc là \\R{2} đưa ra kết quả mong muốn trong Java 9, điều này không nhất quán, vì không sở hữu {n}không nên tắt theo dõi lại. - Holger
Có thể điều này có thể được khắc phục JDK-8176983? - nullpointer


Đó là một lỗi trong Java 8 và nó đã được sửa: JDK-8176029: "Trình kết nối đường kẻ không tương đương với mẫu như đã nêu trong javadoc".

Cũng thấy: Java-8 regex tiêu cực lookbehind với `\ R`


63
2017-12-18 16:11



Thú vị, đối với tôi, hành vi của Java 8 trông rất lành mạnh. Mặc dù có thể diễn giải "\ r \ n" là hai dấu ngắt dòng liên tiếp, nhưng nó có ý nghĩa rất ít như tôi thấy. Nếu bạn có nghĩa là hai dấu ngắt dòng, bạn sẽ viết "\ n \ n" hoặc "\ r \ n \ r \ n", v.v. tương tự dấu ngắt dòng. "\ r \ n" thực sự chỉ có nghĩa là một. - doublep
Nó có ý nghĩa !. Nhưng java 8 có hành vi tôi cần. mmmh. - Germán Bouzas
@ GermánBouzas: Tôi đoán bạn cần bình thường hóa các dấu ngắt hàng đầu tiên, ví dụ: với replaceAll ("\\R", "\\n") (chưa thử nghiệm, nhưng tôi đoán rằng các thay đổi ngược lại sẽ không đóng vai trò nào ở đây). - doublep
Tôi chắc rằng đây là lỗi. \R không được phép quay trở lại; có những lý do vững chắc cho việc này. Tôi sẽ thấy những gì tôi có thể tìm thấy: bạn không bao giờ phải tách một CRLF thành hai trường hợp hoặc \R. - tchrist