2017-12-18 123 views
76

निम्न कोड जावा 8 & 9 दोनों में संकलित करता है, लेकिन अलग-अलग व्यवहार करता है।जावा 8 और जावा 9 के बीच नियमित अभिव्यक्तियों में R अलग-अलग व्यवहार क्यों करता है?

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); 
     } 
    } 
} 

जब मैं यह जावा 8 के साथ चलाने के लिए यह रिटर्न:

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

लेकिन जब मैं जावा 9 के साथ इसे चलाने के उत्पादन अलग है:

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

क्यों?

+3

ऐसा लगता है कि जावा 8 '\ R' लालची है, जबकि 9 में यह नहीं है। – doublep

+0

आपको System.getProperty ("line.separator") से क्या स्ट्रिंग मिलती है? – dasblinkenlight

+2

@dasblinkenlight: इससे कोई फर्क नहीं पड़ता; '\ R' है [लाइनबैक मैचर] (https://docs.oracle.com/javase/9/docs/api/java/util/regex/Pattern.html)। यह ओपी के पास जो कुछ भी होगा उससे मेल खाएगा। – Makoto

उत्तर

46

Java documentation यूनिकोड स्टैंडर्ड के साथ अनुरूपता से बाहर है। जावाडोक गलत है कि \R मिलान करना चाहिए। यह लिखा है:

\R किसी भी यूनिकोड LINEBREAK अनुक्रम, \u000D\u000A|[\u000A\u000B\u000C\u000D\u0085\u2028\u2029]

जावा प्रलेखन गाड़ी है कि के बराबर है। इसके section on R1.6 Line Breaks, Unicode Technical Standard #18 on Regular Expressions में स्पष्ट रूप से कहा गया है:

यह सलाह दी जाती है इस तरह के "\ आर" के रूप में एक नियमित अभिव्यक्ति मेटा-चरित्र हो कि,, (उदाहरण के लिए, # 1 में अक्षर और ऊपर सूचीबद्ध दृश्यों न खत्म होने वाली सभी लाइन मिलान के लिए)। यह निम्नलिखित अभिव्यक्ति के बराबर कुछ के अनुरूप होगा। यह अभिव्यक्ति बैकअप से बचने की आवश्यकता से थोड़ा जटिल है।

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

दूसरे शब्दों में, यह केवल एक दो कोड सूत्री सीआर + वामो (गाड़ी वापसी + linefeed) अनुक्रम वरना यह है कि प्रदान की है कि सेट से एक भी कोड सूत्री मिलान कर सकते हैं केवल एक कैरिज रिटर्न अकेला है जिसके बाद एक लाइनफीड होता है। ऐसा इसलिए है क्योंकि यह को का बैक अप लेने की अनुमति नहीं है। ठीक से काम करने के लिए सीआरएलएफ \R के लिए परमाणु होना चाहिए।

तो जावा 9 अब आर 1.6 की दृढ़ता से अनुशंसा करता है कि अनुरूप नहीं है। इसके अलावा, अब यह कुछ ऐसा कर रहा है जो जावा 8 में नहीं किया गया था, और नहीं किया था।

ऐसा लगता है कि शेरमेन (पढ़ना: जुएमिंग शेन) को फिर से एक हॉलर देने के लिए मेरे लिए समय है। औपचारिक अनुरूपता के इन छोटे-छोटे मामलों पर मैंने उनके साथ काम किया है।

+1

तो '\\ आर' के बजाय या तो '(?> \\ आर) 'या' \\ आर {1} + 'या ओपी के विशिष्ट मामले में उपयोग करने के लिए एक कामकाज होगा, \\ R {2 } \ 'आर \\ आर' के बजाय +'। दिलचस्प बात यह है कि यहां तक ​​कि \\ आर {1} \\ आर {1} 'या' \\ आर {2} 'जावा 9 के तहत वांछित परिणाम दें, जो असंगत है, क्योंकि गैर-स्वामित्व वाली' {n} 'नहीं होना चाहिए बैक-ट्रैकिंग अक्षम करें। – Holger

+0

शायद यह [जेडीके-8176 9 83] (https://bugs.openjdk.java.net/browse/JDK-8176983) के साथ तय किया जा सकता है? – nullpointer

63

यह जावा 8 में एक बग था और यह ठीक हो गया: JDK-8176029 : "Linebreak matcher is not equivalent to the pattern as stated in javadoc"

यह भी देखें: Java-8 regex negative lookbehind with `\R`

+7

दिलचस्प, मेरे लिए जावा 8 व्यवहार saner दिखता है। हालांकि "\ r \ n" को दो लगातार लाइनब्रेक्स के रूप में समझना संभव है, लेकिन मुझे लगता है कि यह थोड़ा सा समझ में आता है। यदि आप दो लाइनब्रैक का मतलब रखते हैं, तो आप "\ n \ n" या "\ r \ n \ r \ n" आदि लिखेंगे, यानी दो * समान * लाइनब्रैक। "\ r \ n" वास्तव में केवल एक मतलब होना चाहिए। – doublep

+2

यह समझ में आता है! लेकिन जावा 8 में मुझे जिस व्यवहार की आवश्यकता थी। Mmmh। –

+3

@ जर्मनबुजास: मुझे लगता है कि आपको पहले लाइनब्रेक्स को सामान्यीकृत करना होगा, उदा। 'replaceAll (" \\ R "," \\ n ") के साथ '(परीक्षण नहीं किया गया है, लेकिन मुझे लगता है कि बैकट्रैकिंग परिवर्तन यहां कोई भूमिका निभाएंगे)। – doublep

संबंधित मुद्दे