जावा 8 में विभाजन परिणाम सरणी की शुरुआत में कभी-कभी खाली तारों को क्यों हटाता है?


110

जावा 8 से पहले जब हम खाली स्ट्रिंग पर विभाजित होते हैं जैसे

String[] tokens = "abc".split("");

विभाजन तंत्र के साथ चिह्नित स्थानों में विभाजित होगा |

|a|b|c|

क्योंकि ""प्रत्येक वर्ण के पहले और बाद में खाली स्थान मौजूद है। इसलिए परिणाम के रूप में यह पहली सरणी में उत्पन्न होगा

["", "a", "b", "c", ""]

और बाद में खाली तारों को हटा देगा (क्योंकि हमने स्पष्ट रूप से limitतर्क को नकारात्मक मूल्य प्रदान नहीं किया था ) इसलिए यह अंत में वापस आ जाएगा

["", "a", "b", "c"]

जावा में 8 विभाजन तंत्र बदल गया है। अब जब हम उपयोग करते हैं

"abc".split("")

हम ["a", "b", "c"]इसके बजाय सरणी प्राप्त करेंगे ["", "a", "b", "c"]ताकि ऐसा लगे कि प्रारंभ में खाली तार भी हटा दिए गए हैं। लेकिन उदाहरण के लिए यह सिद्धांत विफल हो जाता है

"abc".split("a")

शुरू में खाली स्ट्रिंग के साथ सरणी देता है ["", "bc"]

क्या कोई समझा सकता है कि यहाँ क्या चल रहा है और जावा 8 में विभाजन के नियम कैसे बदल गए हैं?


Java8 उसे ठीक करता है। इस बीच, s.split("(?!^)")काम करने लगता है।
shkschneider

2
मेरे प्रश्न में वर्णित @shkschneider व्यवहार पूर्व जावा -8 संस्करणों का बग नहीं है। यह व्यवहार विशेष रूप से बहुत उपयोगी नहीं था, लेकिन यह अभी भी सही था (जैसा कि मेरे प्रश्न में दिखाया गया है), इसलिए हम यह नहीं कह सकते कि यह "निश्चित" था। मैं इसे और अधिक सुधार की तरह देखते हैं तो हम इस्तेमाल कर सकते हैं split("")(जो लोग रेगुलर एक्सप्रेशन का उपयोग नहीं करते के लिए) गुप्त के बजाय split("(?!^)")या split("(?<!^)")या कुछ अन्य लोगों regexes।
Pshemo

1
फेडोरा 21 में उन्नत फ़ेडोरा के बाद एक ही मुद्दे का सामना करना पड़ा, जेडीके 1.8 के साथ फेडोरा 21 जहाजों, और मेरा आईआरसी गेम एप्लिकेशन इस वजह से टूट गया है।
लियूयान

7
यह प्रश्न जावा 8. में इस ब्रेकिंग परिवर्तन का एकमात्र दस्तावेज प्रतीत होता है। ओरेकल ने इसे असंगतताओं की अपनी सूची से बाहर कर दिया ।
शॉन वान गॉर्डर

4
जेडीके में यह बदलाव सिर्फ मुझे ट्रैक करने में 2 घंटे का खर्च है जो गलत है। मेरे कंप्यूटर (JDK8) में कोड ठीक चलता है लेकिन दूसरी मशीन (JDK7) पर रहस्यमय तरीके से विफल हो जाता है। ओरेकल वास्तव में होना चाहिए के प्रलेखन अद्यतन String.split (स्ट्रिंग regex) , बल्कि Pattern.split या String.split की तुलना में (स्ट्रिंग regex, पूर्णांक सीमा) के रूप में इस अब तक का सबसे आम उपयोग कर रहा है। जावा अपने पोर्टेबिलिटी उर्फ ​​तथाकथित काम के लिए जाना जाता है। यह एक बड़ा पिछड़ा-विराम परिवर्तन है और अच्छी तरह से प्रलेखित नहीं है।
PoweredByRice

जवाबों:


84

String.split(जो कॉल करता है Pattern.split) का व्यवहार जावा 7 और जावा 8 के बीच बदलता है।

प्रलेखन

के प्रलेखन के बीच तुलना Pattern.splitमें जावा 7 और जावा 8 , हम निम्नलिखित खंड का निरीक्षण जोड़ा जा रहा है:

जब इनपुट अनुक्रम की शुरुआत में एक सकारात्मक-चौड़ाई का मिलान होता है तो परिणामी सरणी की शुरुआत में एक खाली अग्रणी विकल्प शामिल होता है। शुरुआत में एक शून्य-चौड़ाई मैच हालांकि इस तरह के खाली अग्रणी विकल्प का उत्पादन कभी नहीं करता है।

जावा 7 की तुलना String.splitमें जावा 8 में भी यही क्लॉज जोड़ा गया है ।

संदर्भ कार्यान्वयन

आइए हम Pattern.splitजावा 7 और जावा 8 में संदर्भ इम्प्लांटेशन के कोड की तुलना करते हैं । कोड 7u40-b43 और 8-b132 के लिए grepcode से पुनर्प्राप्त किया जाता है।

जावा 7

public String[] split(CharSequence input, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<>();
    Matcher m = matcher(input);

    // Add segments before each match found
    while(m.find()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}

जावा 8

public String[] split(CharSequence input, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<>();
    Matcher m = matcher(input);

    // Add segments before each match found
    while(m.find()) {
        if (!matchLimited || matchList.size() < limit - 1) {
            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }
            String match = input.subSequence(index, m.start()).toString();
            matchList.add(match);
            index = m.end();
        } else if (matchList.size() == limit - 1) { // last one
            String match = input.subSequence(index,
                                             input.length()).toString();
            matchList.add(match);
            index = m.end();
        }
    }

    // If no match was found, return this
    if (index == 0)
        return new String[] {input.toString()};

    // Add remaining segment
    if (!matchLimited || matchList.size() < limit)
        matchList.add(input.subSequence(index, input.length()).toString());

    // Construct result
    int resultSize = matchList.size();
    if (limit == 0)
        while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
            resultSize--;
    String[] result = new String[resultSize];
    return matchList.subList(0, resultSize).toArray(result);
}

जावा 8 में निम्नलिखित कोड के अलावा इनपुट स्ट्रिंग की शुरुआत में शून्य-लंबाई मैच को बाहर करता है, जो ऊपर दिए गए व्यवहार की व्याख्या करता है।

            if (index == 0 && index == m.start() && m.start() == m.end()) {
                // no empty leading substring included for zero-width match
                // at the beginning of the input char sequence.
                continue;
            }

अनुकूलता बनाए रखना

जावा 8 और इसके बाद के संस्करण में व्यवहार

बनाने के लिए splitलगातार संस्करणों में और जावा 8 में व्यवहार के साथ संगत बर्ताव करता है:

  1. अपने रेगुलर एक्सप्रेशन से तो कर सकते हैं शून्य लंबाई स्ट्रिंग से मेल, बस जोड़ने (?!\A)पर अंत regex की और गैर कैप्चरिंग समूह में मूल regex लपेट (?:...)(यदि आवश्यक हो)।
  2. यदि आपका रेगेक्स शून्य-लंबाई स्ट्रिंग से मेल नहीं खा सकता है , तो आपको कुछ भी करने की आवश्यकता नहीं है।
  3. यदि आप नहीं जानते कि रेगेक्स शून्य-लंबाई स्ट्रिंग से मेल खा सकता है या नहीं, तो चरण 1 में दोनों क्रियाएं करें।

(?!\A) जाँच करता है कि स्ट्रिंग की शुरुआत में स्ट्रिंग समाप्त नहीं होती है, जिसका अर्थ है कि स्ट्रिंग की शुरुआत में मैच एक खाली मैच है।

जावा 7 और पूर्व में व्यवहार के बाद

splitजावा 7 और पूर्व के साथ पिछड़े-संगत बनाने के लिए कोई सामान्य समाधान नहीं है , splitअपने स्वयं के कस्टम कार्यान्वयन को इंगित करने के सभी उदाहरणों की जगह ।


कोई भी विचार कि मैं split("")कोड को कैसे बदल सकता हूं ताकि यह विभिन्न जावा संस्करणों के पार हो?
डैनियल

2
@Daniel: यह इसे आगे-संगत बनाने जोड़कर (जावा 8 के व्यवहार का अनुसरण) संभव है (?!^)करने के लिए अंत regex की और गैर पर कब्जा समूह में मूल regex लपेट (?:...)(यदि आवश्यक हो), लेकिन मैं किसी भी नहीं सोच सकते हैं इसे पिछड़ा-संगत बनाने का तरीका (जावा 7 और पूर्व में पुराने व्यवहार का पालन करें)।
नाहत

स्पष्टीकरण के लिए धन्यवाद। क्या आप वर्णन कर सकते हैं "(?!^)"? किन परिदृश्यों में यह अलग होगा ""? (मैं रेगेक्स पर भयानक हूँ!: - /)।
डैनियल

1
@ डैनियल: इसका अर्थ Pattern.MULTILINEझंडे से प्रभावित होता है , जबकि \Aहमेशा झंडे की परवाह किए बिना स्ट्रिंग की शुरुआत में मेल खाता है।
न्हाथ्ठ

30

के प्रलेखन में यह निर्दिष्ट किया गया है split(String regex, limit)

जब इस स्ट्रिंग की शुरुआत में एक सकारात्मक-चौड़ाई का मिलान होता है, तो परिणामस्वरूप सरणी की शुरुआत में एक खाली अग्रणी विकल्प शामिल होता है। शुरुआत में एक शून्य-चौड़ाई मैच हालांकि इस तरह के खाली अग्रणी विकल्प का उत्पादन कभी नहीं करता है।

में "abc".split("")आप ऐसा अग्रणी खाली स्ट्रिंग जिसके परिणामस्वरूप सरणी में शामिल नहीं है शुरुआत में एक शून्य चौड़ाई मैच मिला है।

हालाँकि आपके दूसरे स्निपेट में जब आप विभाजित होते हैं "a"तो आपको एक सकारात्मक चौड़ाई वाला मैच (इस मामले में 1) मिलता है, इसलिए उम्मीद के मुताबिक खाली प्रमुख विकल्प को शामिल किया जाता है।

(हटा दिया गया अप्रासंगिक स्रोत कोड)


3
यह केवल एक सवाल है। क्या जेडीके से कोड का एक टुकड़ा पोस्ट करना ठीक है? Google के साथ कॉपीराइट समस्या को याद रखें - हैरी पॉटर - ओरेकल?
पॉल वर्गास

6
@PaVVargas निष्पक्ष होने के लिए मुझे नहीं पता, लेकिन मुझे लगता है कि यह ठीक है क्योंकि आप JDK डाउनलोड कर सकते हैं, और src फ़ाइल को खोल सकते हैं जिसमें सभी स्रोत हैं। इसलिए तकनीकी रूप से हर कोई स्रोत देख सकता था।
एलेक्सिस सी।

12
@PaulVargas "ओपन सोर्स" में "ओपन" कुछ के लिए खड़ा है।
मार्को टोपोलनिक

2
@ZouZou: सिर्फ इसलिए कि हर कोई देख सकता है इसका मतलब यह नहीं है कि आप इसे फिर से प्रकाशित कर सकते हैं
user102008

2
@Paul वर्गास, IANAL लेकिन कई अन्य अवसरों में इस प्रकार का एक पद उद्धरण / उचित उपयोग की स्थिति में आता है। विषय पर अधिक यहाँ है: meta.stackexchange.com/questions/12527/…
एलेक्स पक्का

14

split()Java 7 से Java 8. के लिए डॉक्स में थोड़ा बदलाव आया था । विशेष रूप से, निम्नलिखित कथन जोड़ा गया था:

जब इस स्ट्रिंग की शुरुआत में एक सकारात्मक-चौड़ाई का मिलान होता है, तो परिणामस्वरूप सरणी की शुरुआत में एक खाली अग्रणी विकल्प शामिल होता है। शुरुआत में एक शून्य-चौड़ाई मैच हालांकि इस तरह के खाली अग्रणी विकल्प का उत्पादन कभी नहीं करता है।

(जोर मेरा)

खाली स्ट्रिंग विभाजन शुरुआत में एक शून्य-चौड़ाई वाला मैच बनाता है, इसलिए एक रिक्त स्ट्रिंग को ऊपर निर्दिष्ट किए गए अनुसार परिणामी सरणी की शुरुआत में शामिल नहीं किया जाता है। इसके विपरीत, आपका दूसरा उदाहरण जो स्ट्रिंग की शुरुआत में "a"एक पॉजिटिव- इनवॉइस मैच उत्पन्न करता है , इसलिए एक खाली स्ट्रिंग वास्तव में परिणामी सरणी की शुरुआत में शामिल है।


कुछ और सेकंडों में फर्क पड़ा।
पॉल वर्गास

2
@PaVVargas वास्तव में यहाँ ZshaZou से कुछ सेकंड पहले ही arshajii ने उत्तर पोस्ट किया था, लेकिन दुर्भाग्य से ZouZou ने मेरे प्रश्न का उत्तर यहाँ दिया । मैं सोच रहा था कि क्या मुझे यह सवाल पूछना चाहिए क्योंकि मुझे पहले से ही एक जवाब पता था लेकिन यह दिलचस्प लग रहा था और ZouZou पहले की टिप्पणी के लिए कुछ प्रतिष्ठा के हकदार थे।
साहीमो

5
नया व्यवहार अधिक तार्किक लगने के बावजूद , यह स्पष्ट रूप से एक पिछड़ी संगतता है । इस बदलाव का एकमात्र औचित्य यह है कि "some-string".split("")यह काफी दुर्लभ मामला है।
ivstas

4
.split("")कुछ भी मिलान के बिना विभाजित करने का एकमात्र तरीका नहीं है। हमने एक सकारात्मक लुकहेड रेगेक्स का उपयोग किया जो कि jdk7 में था जो शुरुआत में भी मेल खाता था और एक खाली सिर तत्व का उत्पादन करता था जो अब चला गया है। github.com/spray/spray/commit/…
jrudolph
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.