एक स्ट्रिंग को कैसे विभाजित किया जाए, लेकिन सीमांकक भी रखें?


243

मेरे पास एक मल्टीलाइन स्ट्रिंग है जो विभिन्न सीमांकक के एक सेट द्वारा सीमांकित है:

(Text1)(DelimiterA)(Text2)(DelimiterC)(Text3)(DelimiterB)(Text4)

मैं इस स्ट्रिंग को इसके भागों में विभाजित कर सकता हूं, उपयोग String.splitकर सकता हूं, लेकिन ऐसा लगता है कि मुझे वास्तविक स्ट्रिंग नहीं मिल सकती है, जो सीमांकक regex से मेल खाती है।

दूसरे शब्दों में, यह वही है जो मुझे मिलता है:

  • Text1
  • Text2
  • Text3
  • Text4

यह वही चीज है जो मैं चाहता हूं

  • Text1
  • DelimiterA
  • Text2
  • DelimiterC
  • Text3
  • DelimiterB
  • Text4

क्या कोई सीमांकक रेगीक्स का उपयोग करके स्ट्रिंग को विभाजित करने का कोई जेडीके तरीका है लेकिन साथ ही सीमांकक भी रखता है?


यह सोचने के लिए आओ, आप सीमांकक कहाँ रखना चाहते हैं? शब्दों के साथ या अलग? पहले मामले में, क्या आप उन्हें पूर्ववर्ती या निम्नलिखित शब्द से जोड़ेंगे? दूसरे मामले में, मेरा जवाब है कि आपको क्या चाहिए ...
फील्हो

बस एक वर्ग लागू किया है जो आपको वह चीज़ हासिल करने में मदद करे जो आप खोज रहे हैं। नीचे देखें
VonC

जवाबों:


366

आप Lookahead और Lookbehind का उपयोग कर सकते हैं। ऐशे ही:

System.out.println(Arrays.toString("a;b;c;d".split("(?<=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("(?=;)")));
System.out.println(Arrays.toString("a;b;c;d".split("((?<=;)|(?=;))")));

और आपको मिलेगा:

[a;, b;, c;, d]
[a, ;b, ;c, ;d]
[a, ;, b, ;, c, ;, d]

आखिरी वही जो आप चाहते हैं।

((?<=;)|(?=;))पहले ;या बाद में एक खाली चरित्र का चयन करने के लिए बराबर है ;

उम्मीद है की यह मदद करेगा।

पठनीयता पर EDIT फैबियन Steeg टिप्पणी वैध है। पठनीयता हमेशा RegEx के लिए समस्या है। एक बात, मैं यह आसान बनाने में मदद करने के लिए एक चर बनाने के लिए जिसका नाम प्रतिनिधित्व करता है कि रेगेक्स क्या करता है और उसकी मदद करने के लिए जावा स्ट्रिंग प्रारूप का उपयोग करता है। ऐशे ही:

static public final String WITH_DELIMITER = "((?<=%1$s)|(?=%1$s))";
...
public void someMethod() {
...
final String[] aEach = "a;b;c;d".split(String.format(WITH_DELIMITER, ";"));
...
}
...

इससे थोड़ी मदद मिलती है। :-D


2
बहुत अच्छा! यहाँ हम नियमित भावों की शक्ति को फिर से देख सकते हैं !!
जॉर्ज

1
यह देखकर अच्छा लगा कि स्ट्रिंग # विभाजन के साथ ऐसा करने का एक तरीका है, हालांकि मैं चाहता हूं कि इसमें एक तरीका भी शामिल था कि सीमांकक को शामिल किया जाए क्योंकि स्ट्रिंगरटोकेनाइज़र के लिए यह split(";", true)बहुत अधिक पठनीय होगा split("((?<=;)|(?=;))")
फेबियन स्टील

3
यह होना चाहिए: String.format(WITH_DELIMITER, ";");प्रारूप एक स्थिर विधि है।
john16384

8
एक जटिलता जो मुझे अभी [\\s,]+मिली, वह है चर-लंबाई परिसीमन (कहना ) कि आप पूरी तरह से मेल खाना चाहते हैं। आवश्यक रेगेक्स और भी लंबे समय तक मिलते हैं, क्योंकि आपको अतिरिक्त नकारात्मक रूप की आवश्यकता होती है {आगे, पीछे} उन्हें बीच में मिलान करने से बचने के लिए, उदा। (?<=[\\s,]+)(?![\\s,])|(?<![\\s,])(?=[\\s,]+)
माइकल पोलित्स्की

3
क्या होगा अगर मैं दो सीमांकक द्वारा विभाजित करना चाहता हूं? हम कहते हैं ';' या '।'
चमत्कार-दोह

78

आप लुकरॉइड्स का उपयोग करना चाहते हैं, और शून्य-चौड़ाई के मैचों पर विभाजन करना चाहते हैं। यहाँ कुछ उदाहरण हैं:

public class SplitNDump {
    static void dump(String[] arr) {
        for (String s : arr) {
            System.out.format("[%s]", s);
        }
        System.out.println();
    }
    public static void main(String[] args) {
        dump("1,234,567,890".split(","));
        // "[1][234][567][890]"
        dump("1,234,567,890".split("(?=,)"));   
        // "[1][,234][,567][,890]"
        dump("1,234,567,890".split("(?<=,)"));  
        // "[1,][234,][567,][890]"
        dump("1,234,567,890".split("(?<=,)|(?=,)"));
        // "[1][,][234][,][567][,][890]"

        dump(":a:bb::c:".split("(?=:)|(?<=:)"));
        // "[][:][a][:][bb][:][:][c][:]"
        dump(":a:bb::c:".split("(?=(?!^):)|(?<=:)"));
        // "[:][a][:][bb][:][:][c][:]"
        dump(":::a::::b  b::c:".split("(?=(?!^):)(?<!:)|(?!:)(?<=:)"));
        // "[:::][a][::::][b  b][::][c][:]"
        dump("a,bb:::c  d..e".split("(?!^)\\b"));
        // "[a][,][bb][:::][c][  ][d][..][e]"

        dump("ArrayIndexOutOfBoundsException".split("(?<=[a-z])(?=[A-Z])"));
        // "[Array][Index][Out][Of][Bounds][Exception]"
        dump("1234567890".split("(?<=\\G.{4})"));   
        // "[1234][5678][90]"

        // Split at the end of each run of letter
        dump("Boooyaaaah! Yippieeee!!".split("(?<=(?=(.)\\1(?!\\1))..)"));
        // "[Booo][yaaaa][h! Yipp][ieeee][!!]"
    }
}

और हाँ, यह है कि पिछले पैटर्न में वहाँ नेस्टेड नेस्टेड दावे है।

संबंधित सवाल

यह सभी देखें


1
ध्यान दें कि यह केवल अपेक्षाकृत सरल अभिव्यक्तियों के लिए काम करेगा; मुझे एक "लुक-पीछे समूह के पास एक स्पष्ट अधिकतम लंबाई नहीं है" सभी वास्तविक संख्याओं का प्रतिनिधित्व करने वाले रेगेक्स के साथ इसका उपयोग करने की कोशिश कर रहा है।
daveagp


30

एक बहुत ही भोले समाधान, जिसमें रेगेक्स शामिल नहीं है, की तर्ज पर अपने सीमांकक की जगह एक स्ट्रिंग का प्रदर्शन करना होगा (परिसीमन के लिए अल्पविराम मानकर):

string.replace(FullString, "," , "~,~")

जहां आप एक उपयुक्त अनूठे सीमांकक के साथ टिल्डा (~) को बदल सकते हैं।

फिर यदि आप अपने नए परिसीमन पर एक विभाजन करते हैं तो मुझे विश्वास है कि आपको वांछित परिणाम मिलेगा।


24
import java.util.regex.*;
import java.util.LinkedList;

public class Splitter {
    private static final Pattern DEFAULT_PATTERN = Pattern.compile("\\s+");

    private Pattern pattern;
    private boolean keep_delimiters;

    public Splitter(Pattern pattern, boolean keep_delimiters) {
        this.pattern = pattern;
        this.keep_delimiters = keep_delimiters;
    }
    public Splitter(String pattern, boolean keep_delimiters) {
        this(Pattern.compile(pattern==null?"":pattern), keep_delimiters);
    }
    public Splitter(Pattern pattern) { this(pattern, true); }
    public Splitter(String pattern) { this(pattern, true); }
    public Splitter(boolean keep_delimiters) { this(DEFAULT_PATTERN, keep_delimiters); }
    public Splitter() { this(DEFAULT_PATTERN); }

    public String[] split(String text) {
        if (text == null) {
            text = "";
        }

        int last_match = 0;
        LinkedList<String> splitted = new LinkedList<String>();

        Matcher m = this.pattern.matcher(text);

        while (m.find()) {

            splitted.add(text.substring(last_match,m.start()));

            if (this.keep_delimiters) {
                splitted.add(m.group());
            }

            last_match = m.end();
        }

        splitted.add(text.substring(last_match));

        return splitted.toArray(new String[splitted.size()]);
    }

    public static void main(String[] argv) {
        if (argv.length != 2) {
            System.err.println("Syntax: java Splitter <pattern> <text>");
            return;
        }

        Pattern pattern = null;
        try {
            pattern = Pattern.compile(argv[0]);
        }
        catch (PatternSyntaxException e) {
            System.err.println(e);
            return;
        }

        Splitter splitter = new Splitter(pattern);

        String text = argv[1];
        int counter = 1;
        for (String part : splitter.split(text)) {
            System.out.printf("Part %d: \"%s\"\n", counter++, part);
        }
    }
}

/*
    Example:
    > java Splitter "\W+" "Hello World!"
    Part 1: "Hello"
    Part 2: " "
    Part 3: "World"
    Part 4: "!"
    Part 5: ""
*/

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

संपादित करें: निश्चित सीमा के मामले। परीक्षण मामलों के साथ टिप्पणी किए गए स्रोत यहां देखे जा सकते हैं: http://snippets.dzone.com/posts/show/6453


वाह ... भाग लेने के लिए धन्यवाद! दिलचस्प दृष्टिकोण। मुझे यकीन नहीं है कि यह लगातार मदद कर सकता है (इसके साथ, कभी-कभी एक सीमांकक होता है, कभी-कभी ऐसा नहीं होता है), लेकिन प्रयास के लिए +1। हालाँकि, आपको अभी भी सीमा मामलों (खाली या अशक्त मूल्यों) को ठीक से संबोधित करने की आवश्यकता है
VONC

मैं आपको इस वर्ग को सही ढंग से सुदृढ़ करने के लिए आमंत्रित करता हूं, इसे अच्छी तरह से प्रलेखित करता हूं, फाइंडबग्स और चेकस्टाइल के साथ एक पास बनाता हूं, और फिर इसे स्निपेट वेबसाइट पर प्रकाशित करता हूं (इस पेज को टन के कोड के साथ अव्यवस्थित करने से बचने के लिए)
VONC

आपने चुनौती जीती! इर्रर ... बधाई! जैसा कि आप जानते हैं, कोड-चैलेंज थ्रेड से, उसके लिए कोई विशेष बिंदु या बैज नहीं होगा ... (sigh): stackoverflow.com/questions/172184 । लेकिन इस योगदान के लिए धन्यवाद।
वॉन सी सी

@VonC अधिकांश समय, nullतर्क पर NPE को फेंकना सही तरीका है। चुपचाप इसे संभालने से बाद में गलतियाँ दिखाई देती हैं।
मातरिनस

@maaartinus मैं सहमत हूं, लेकिन निश्चित रूप से ऐसे उदाहरण हैं जहां आप सिर्फ एनपीई की तुलना में अधिक उपयोगकर्ता-अनुकूल संदेश फेंकना चाहते हैं, है ना?
वॉनसी

11

मैं यहां देर से पहुंचा, लेकिन मूल प्रश्न पर लौटते हुए, सिर्फ लुकर का उपयोग क्यों नहीं किया गया?

Pattern p = Pattern.compile("(?<=\\w)(?=\\W)|(?<=\\W)(?=\\w)");
System.out.println(Arrays.toString(p.split("'ab','cd','eg'")));
System.out.println(Arrays.toString(p.split("boo:and:foo")));

उत्पादन:

[', ab, ',', cd, ',', eg, ']
[boo, :, and, :, foo]

संपादित करें: आप जो ऊपर देखते हैं, वह वही है जो कमांड लाइन पर दिखाई देता है जब मैं उस कोड को चलाता हूं, लेकिन मैं अब देखता हूं कि यह थोड़ा भ्रमित है। यह ध्यान रखना मुश्किल है कि कौन से कॉमा परिणाम का हिस्सा हैं और कौन से जोड़े गए हैं Arrays.toString()। SO का सिंटैक्स हाइलाइटिंग या तो मदद नहीं कर रहा है। मेरे खिलाफ काम करने के बजाय मेरे साथ काम करने के लिए हाइलाइटिंग प्राप्त करने की उम्मीद में, यहाँ उन सरणियों को कैसे देखा जाएगा मैं उन्हें स्रोत कोड में घोषित कर रहा था:

{ "'", "ab", "','", "cd", "','", "eg", "'" }
{ "boo", ":", "and", ":", "foo" }

मुझे आशा है कि यह पढ़ना आसान है। हेड-अप के लिए धन्यवाद, @finnw।


मुझे पता है कि यह गलत लग रहा है - यह मेरे लिए गलत लग रहा था जब मैं इस तथ्य के एक साल बाद अभी वापस आया। नमूना इनपुट खराब तरीके से चुना गया था; मैं पोस्ट संपादित करूंगा और चीजों को स्पष्ट करने का प्रयास करूंगा।
एलन मूर


10

मैं जानता हूं कि यह बहुत पुराना सवाल है और इसका उत्तर भी स्वीकार कर लिया गया है। लेकिन फिर भी मैं मूल प्रश्न का बहुत ही सरल उत्तर प्रस्तुत करना चाहूंगा। इस कोड पर विचार करें:

String str = "Hello-World:How\nAre You&doing";
inputs = str.split("(?!^)\\b");
for (int i=0; i<inputs.length; i++) {
   System.out.println("a[" + i + "] = \"" + inputs[i] + '"');
}

उत्पादन:

a[0] = "Hello"
a[1] = "-"
a[2] = "World"
a[3] = ":"
a[4] = "How"
a[5] = "
"
a[6] = "Are"
a[7] = " "
a[8] = "You"
a[9] = "&"
a[10] = "doing"

मैं केवल शब्द सीमा \bका उपयोग शब्दों को परिसीमित करने के लिए कर रहा हूं , सिवाय इसके कि जब पाठ शुरू हो।


1
+1 मेरे लिए सबसे अच्छा जवाब। लेकिन यह अल्फ़ान्यूमेरिक स्ट्रिंग में अल्फ़ान्यूमेरिक सीमांकक के लिए काम नहीं करता है
कासिमिर एट हिप्पोलाइट

@ कसीमिरहिप्पोलीटे: अपने उत्थान के लिए धन्यवाद। क्या आप एक नमूना इनपुट प्रदान कर सकते हैं जहाँ यह काम नहीं किया।
शुभ

2
उदाहरण के लिए इस के लिए काम नहीं करता है abcdefके साथ deपरिसीमक के रूप में है, लेकिन आप समस्या का उपयोग कर हल कर सकते हैं(?!^|$)(?:(?<=de)(?!de)|(?<!de)(?=de))
Casimir एट Hippolyte

1
परिणाम में एक खाली स्ट्रिंग से बचने के लिए पहला जोर नोट करें जब स्ट्रिंग सीमांकक के साथ समाप्त होता है, अर्थात(?!^|$)
कासिमिर एट हिप्पोलाइट


9

उपरोक्त जवाबों पर मेरी नज़र थी और ईमानदारी से उनमें से कोई भी मुझे संतोषजनक नहीं लगता। आप जो करना चाहते हैं वह अनिवार्य रूप से पर्ल विभाजन की कार्यक्षमता की नकल करता है। क्यों जावा यह अनुमति नहीं देता है और एक जुड़ने () विधि कहीं न कहीं मेरे से परे है लेकिन मैं पचाता हूं। तुम भी वास्तव में इस के लिए एक वर्ग की जरूरत नहीं है। इसका सिर्फ एक फंक्शन है। यह नमूना कार्यक्रम चलाएं:

पहले के कुछ उत्तरों की अत्यधिक अशक्तता है, जिन्हें मैंने हाल ही में एक प्रश्न के उत्तर में लिखा है:

https://stackoverflow.com/users/18393/cletus

वैसे भी, कोड:

public class Split {
    public static List<String> split(String s, String pattern) {
        assert s != null;
        assert pattern != null;
        return split(s, Pattern.compile(pattern));
    }

    public static List<String> split(String s, Pattern pattern) {
        assert s != null;
        assert pattern != null;
        Matcher m = pattern.matcher(s);
        List<String> ret = new ArrayList<String>();
        int start = 0;
        while (m.find()) {
            ret.add(s.substring(start, m.start()));
            ret.add(m.group());
            start = m.end();
        }
        ret.add(start >= s.length() ? "" : s.substring(start));
        return ret;
    }

    private static void testSplit(String s, String pattern) {
        System.out.printf("Splitting '%s' with pattern '%s'%n", s, pattern);
        List<String> tokens = split(s, pattern);
        System.out.printf("Found %d matches%n", tokens.size());
        int i = 0;
        for (String token : tokens) {
            System.out.printf("  %d/%d: '%s'%n", ++i, tokens.size(), token);
        }
        System.out.println();
    }

    public static void main(String args[]) {
        testSplit("abcdefghij", "z"); // "abcdefghij"
        testSplit("abcdefghij", "f"); // "abcde", "f", "ghi"
        testSplit("abcdefghij", "j"); // "abcdefghi", "j", ""
        testSplit("abcdefghij", "a"); // "", "a", "bcdefghij"
        testSplit("abcdefghij", "[bdfh]"); // "a", "b", "c", "d", "e", "f", "g", "h", "ij"
    }
}

मैं भ्रमित हूं: जावा में एक विभाजन () विधि है, जो पर्ल के मॉडल पर है, लेकिन बहुत कम शक्तिशाली है। यहाँ समस्या यह है कि जावा का विभाजन () सीमांकक को वापस करने के लिए कोई रास्ता नहीं प्रदान करता है, जिसे आप कोष्ठक पर कब्जा करने में रेक्स को संलग्न करके पर्ल में प्राप्त कर सकते हैं।
एलन मूर


7

मुझे StringTokenizer का विचार पसंद है क्योंकि यह Enumerable है।
लेकिन यह अप्रचलित भी है, और String.split द्वारा प्रतिस्थापित किया जाता है जो एक उबाऊ स्ट्रिंग लौटाता है [] (और इसमें व्यंजनों को शामिल नहीं किया गया है)।

इसलिए मैंने एक StringTokenizerEx लागू किया, जो कि एक Iterable है, और जो एक स्ट्रिंग को विभाजित करने के लिए एक वास्तविक regexp लेता है।

एक वास्तविक रीजैक्सप का अर्थ है कि यह एक 'चरित्र अनुक्रम' नहीं है जो कि सीमांकक बनाने के लिए दोहराया गया है:
'ओ' केवल 'ओ' से मेल खाएगा, और 'ओओ' को तीन सीमांकक में विभाजित करेगा, जिसमें दो खाली स्ट्रिंग होंगे:

[o], '', [o], '', [o]

लेकिन रेगेक्स ओ + "एओओब" को विभाजित करते समय अपेक्षित परिणाम लौटाएगा

[], 'a', [ooo], 'b', []

इस StringTokenizerEx का उपयोग करने के लिए:

final StringTokenizerEx aStringTokenizerEx = new StringTokenizerEx("boo:and:foo", "o+");
final String firstDelimiter = aStringTokenizerEx.getDelimiter();
for(String aString: aStringTokenizerEx )
{
    // uses the split String detected and memorized in 'aString'
    final nextDelimiter = aStringTokenizerEx.getDelimiter();
}

इस क्लास का कोड DZone स्निपेट्स पर उपलब्ध है ।

हमेशा की तरह एक कोड-चैलेंज प्रतिक्रिया (परीक्षण मामलों के साथ एक स्व-निहित वर्ग), कॉपी-पेस्ट करें ('src / test' निर्देशिका में) और इसे चलाएं । इसकी मुख्य () विधि अलग-अलग उपयोगों को दर्शाती है।


नोट: (२०० ९ के अंत में संपादित करें)

लेख अंतिम विचार: जावा गूढ़ व्यक्ति: विभाजन मेल एक अच्छा काम करता है जिसमें विचित्र व्यवहार की व्याख्या की गई है String.split()
जोश बलोच ने भी उस लेख के जवाब में टिप्पणी की:

हाँ, यह एक दर्द है। FWIW, यह एक बहुत अच्छे कारण के लिए किया गया था: पर्ल के साथ संगतता।
जिस आदमी ने यह किया वह माइक "मैडबॉट" मैकक्लोस्की है, जो अब Google में हमारे साथ काम करता है। माइक ने सुनिश्चित किया कि जावा की नियमित अभिव्यक्तियाँ लगभग हर 30K पर्ल नियमित अभिव्यक्ति परीक्षणों (और तेज़ी से चली) में से प्रत्येक में उत्तीर्ण हुईं।

Google की सामान्य लाइब्रेरी अमरूद में एक स्प्लिटर भी होता है जो है:

  • उपयोग करने के लिए सरल
  • Google द्वारा अनुरक्षित (और आपके द्वारा नहीं)

तो यह जाँच के लायक हो सकता है। उनके प्रारंभिक मोटे दस्तावेज (पीडीएफ) से :

JDK के पास यह है:

String[] pieces = "foo.bar".split("\\.");

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

मिनी-गूढ़ व्यक्ति: ", ए, बी,"। विभाजन (",") रिटर्न ...

(a) "", "a", "", "b", ""
(b) null, "a", null, "b", null
(c) "a", null, "b"
(d) "a", "b"
(e) None of the above

उत्तर: (e) उपरोक्त में से कोई नहीं।

",a,,b,".split(",")
returns
"", "a", "", "b"

केवल अनुगामी खाली जगह छोड़ रहे हैं! (जो लंघन को रोकने के लिए समाधान जानता है; यह एक मजेदार है ...)

किसी भी मामले में, हमारी फाड़नेवाला बस अधिक लचीली है: डिफ़ॉल्ट व्यवहार सरल है:

Splitter.on(',').split(" foo, ,bar, quux,")
--> [" foo", " ", "bar", " quux", ""]

यदि आप अतिरिक्त सुविधाएँ चाहते हैं, तो उनके लिए पूछें!

Splitter.on(',')
.trimResults()
.omitEmptyStrings()
.split(" foo, ,bar, quux,")
--> ["foo", "bar", "quux"]

विन्यास विधियों का क्रम मायने नहीं रखता है - बंटवारे के दौरान, खाली करने के लिए जाँच करने से पहले ट्रिमिंग होता है।



6

3 aurgument को "सत्य" के रूप में पास करें। यह सीमांकक भी लौटेगा।

StringTokenizer(String str, String delimiters, true);

4

यहां एक सरल स्वच्छ कार्यान्वयन है जो Pattern#splitचर लंबाई पैटर्न के साथ संगत है और काम करता है, जो पीछे नहीं दिखता है, और इसका उपयोग करना आसान है। यह @ क्लेटस द्वारा प्रदान किए गए समाधान के समान है ।

public static String[] split(CharSequence input, String pattern) {
    return split(input, Pattern.compile(pattern));
}

public static String[] split(CharSequence input, Pattern pattern) {
    Matcher matcher = pattern.matcher(input);
    int start = 0;
    List<String> result = new ArrayList<>();
    while (matcher.find()) {
        result.add(input.subSequence(start, matcher.start()).toString());
        result.add(matcher.group());
        start = matcher.end();
    }
    if (start != input.length()) result.add(input.subSequence(start, input.length()).toString());
    return result.toArray(new String[0]);
}

मैं यहाँ अशक्त जाँच Pattern#splitनहीं करता, मैं क्यों नहीं करना चाहिए। मैं ifअंत में पसंद नहीं है, लेकिन यह के साथ स्थिरता के लिए आवश्यक है Pattern#split। अन्यथा मैं बिना शर्त के आवेदन करूंगा, जिसके परिणामस्वरूप रिक्त स्ट्रिंग परिणाम के अंतिम तत्व के रूप में होगी यदि इनपुट स्ट्रिंग पैटर्न के साथ समाप्त होती है।

मैं स्ट्रिंग [] के साथ सुसंगतता में परिवर्तित करता Pattern#splitहूं, मैं new String[0]इसके बजाय उपयोग करता हूं new String[result.size()], यहां देखें कि क्यों।

यहाँ मेरे परीक्षण हैं:

@Test
public void splitsVariableLengthPattern() {
    String[] result = Split.split("/foo/$bar/bas", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/", "$bar", "/bas" }, result);
}

@Test
public void splitsEndingWithPattern() {
    String[] result = Split.split("/foo/$bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/", "$bar" }, result);
}

@Test
public void splitsStartingWithPattern() {
    String[] result = Split.split("$foo/bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "", "$foo", "/bar" }, result);
}

@Test
public void splitsNoMatchesPattern() {
    String[] result = Split.split("/foo/bar", "\\$\\w+");
    Assert.assertArrayEquals(new String[] { "/foo/bar" }, result);
}

2

मैं अपने काम करने के संस्करणों को भी पोस्ट करूंगा (पहले वास्तव में मार्कस के समान है)।

public static String[] splitIncludeDelimeter(String regex, String text){
    List<String> list = new LinkedList<>();
    Matcher matcher = Pattern.compile(regex).matcher(text);

    int now, old = 0;
    while(matcher.find()){
        now = matcher.end();
        list.add(text.substring(old, now));
        old = now;
    }

    if(list.size() == 0)
        return new String[]{text};

    //adding rest of a text as last element
    String finalElement = text.substring(old);
    list.add(finalElement);

    return list.toArray(new String[list.size()]);
}

और यहाँ दूसरा समाधान है और इसका दौर पहले वाले की तुलना में 50% तेज है:

public static String[] splitIncludeDelimeter2(String regex, String text){
    List<String> list = new LinkedList<>();
    Matcher matcher = Pattern.compile(regex).matcher(text);

    StringBuffer stringBuffer = new StringBuffer();
    while(matcher.find()){
        matcher.appendReplacement(stringBuffer, matcher.group());
        list.add(stringBuffer.toString());
        stringBuffer.setLength(0); //clear buffer
    }

    matcher.appendTail(stringBuffer); ///dodajemy reszte  ciagu
    list.add(stringBuffer.toString());

    return list.toArray(new String[list.size()]);
}

2

रेगेक्स का उपयोग कर एक और उम्मीदवार समाधान। टोकन क्रम को बनाए रखता है, एक पंक्ति में एक ही प्रकार के कई टोकन से सही ढंग से मेल खाता है। नकारात्मक पक्ष यह है कि रेगेक्स एक प्रकार का गंदा है।

package javaapplication2;

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

public class JavaApplication2 {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        String num = "58.5+variable-+98*78/96+a/78.7-3443*12-3";

        // Terrifying regex:
        //  (a)|(b)|(c) match a or b or c
        // where
        //   (a) is one or more digits optionally followed by a decimal point
        //       followed by one or more digits: (\d+(\.\d+)?)
        //   (b) is one of the set + * / - occurring once: ([+*/-])
        //   (c) is a sequence of one or more lowercase latin letter: ([a-z]+)
        Pattern tokenPattern = Pattern.compile("(\\d+(\\.\\d+)?)|([+*/-])|([a-z]+)");
        Matcher tokenMatcher = tokenPattern.matcher(num);

        List<String> tokens = new ArrayList<>();

        while (!tokenMatcher.hitEnd()) {
            if (tokenMatcher.find()) {
                tokens.add(tokenMatcher.group());
            } else {
                // report error
                break;
            }
        }

        System.out.println(tokens);
    }
}

नमूना उत्पादन:

[58.5, +, variable, -, +, 98, *, 78, /, 96, +, a, /, 78.7, -, 3443, *, 12, -, 3]

1

मुझे जावा एपीआई में एक मौजूदा फ़ंक्शन का पता नहीं है जो ऐसा करता है (जो यह कहना नहीं है कि यह मौजूद नहीं है), लेकिन यहां मेरा खुद का कार्यान्वयन (एक या अधिक सीमांकक एक एकल टोकन के रूप में वापस आ जाएगा; यदि आप चाहें तो प्रत्येक परिसीमन को एक अलग टोकन के रूप में लौटाया जाना चाहिए, इसके लिए थोड़े अनुकूलन की आवश्यकता होगी):

static String[] splitWithDelimiters(String s) {
    if (s == null || s.length() == 0) {
        return new String[0];
    }
    LinkedList<String> result = new LinkedList<String>();
    StringBuilder sb = null;
    boolean wasLetterOrDigit = !Character.isLetterOrDigit(s.charAt(0));
    for (char c : s.toCharArray()) {
        if (Character.isLetterOrDigit(c) ^ wasLetterOrDigit) {
            if (sb != null) {
                result.add(sb.toString());
            }
            sb = new StringBuilder();
            wasLetterOrDigit = !wasLetterOrDigit;
        }
        sb.append(c);
    }
    result.add(sb.toString());
    return result.toArray(new String[0]);
}


1

मैं पैटर्न और माचिस का उपयोग करने का सुझाव देता हूं, जो लगभग निश्चित रूप से आप जो चाहते हैं वह प्राप्त करेंगे। आपकी नियमित अभिव्यक्ति को String.split में आपके द्वारा उपयोग किए जा रहे से कुछ अधिक जटिल होने की आवश्यकता होगी।


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

1

मुझे नहीं लगता कि यह संभव है String#split, लेकिन आप ए का उपयोग कर सकते हैं StringTokenizer, हालांकि यह आपको अपने सीमांकक को रेगेक्स के रूप में परिभाषित करने की अनुमति नहीं देगा, लेकिन केवल एकल-अंक वर्णों के एक वर्ग के रूप में:

new StringTokenizer("Hello, world. Hi!", ",.!", true); // true for returnDelims

वहां मैं अपने सीमांकक को निर्दिष्ट करने के लिए एक रेगीक्स को परिभाषित नहीं कर सकता।
डैनियल रिकोवस्की

1
StringTokenizer केवल एकल-वर्ण सीमांकक के लिए अनुमति देता है, हालांकि।
माइकल बॉर्गवर्ड

1

यदि आप खर्च कर सकते हैं, तो जावा की जगह (चारसेंसेंस टारगेट, चारसपेंसेंस रिप्लेसमेंट) पद्धति का उपयोग करें और इसके साथ विभाजित करने के लिए एक और सीमांकक भरें। उदाहरण: मैं "दाहिनी ओर" स्ट्रिंग को "बू: और: फू" में विभाजित करना चाहता हूं और ':' रखना चाहता हूं।

String str = "boo:and:foo";
str = str.replace(":","newdelimiter:");
String[] tokens = str.split("newdelimiter");

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



0

तेजी से उत्तर: विभाजित करने के लिए \ b जैसे गैर भौतिक सीमा का उपयोग करें। मैं यह देखने और प्रयोग करने की कोशिश करूंगा कि क्या यह काम करता है (PHP और JS में इसका उपयोग किया गया है)।

यह संभव है, और काम की तरह है, लेकिन बहुत अधिक विभाजित हो सकता है। वास्तव में, यह उस स्ट्रिंग पर निर्भर करता है जिसे आप विभाजित करना चाहते हैं और परिणाम आपको चाहिए। अधिक जानकारी दें, हम आपकी बेहतर मदद करेंगे।

दूसरा तरीका यह है कि आप अपना स्वयं का विभाजन करें, सीमांकक को कैप्चर करें (यह परिवर्तनशील है) और परिणाम के बाद इसे जोड़ दें।

मेरा त्वरित परीक्षण:

String str = "'ab','cd','eg'";
String[] stra = str.split("\\b");
for (String s : stra) System.out.print(s + "|");
System.out.println();

परिणाम:

'|ab|','|cd|','|eg|'|

बहुत ज्यादा... :-)



0

सूची में मिलान किए गए पैटर्न को शामिल करने के लिए Tweaked Pattern.split ()

जोड़ा गया

// add match to the list
        matchList.add(input.subSequence(start, end).toString());

पूर्ण स्रोत

public static String[] inclusiveSplit(String input, String re, int limit) {
    int index = 0;
    boolean matchLimited = limit > 0;
    ArrayList<String> matchList = new ArrayList<String>();

    Pattern pattern = Pattern.compile(re);
    Matcher m = pattern.matcher(input);

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


0

यदि यह मदद करता है तो ऊपर दिए गए कुछ कोड के आधार पर एक ग्रूवी संस्करण है। वैसे भी यह छोटा है। सशर्त रूप से सिर और पूंछ शामिल हैं (यदि वे खाली नहीं हैं)। अंतिम भाग एक डेमो / टेस्ट केस है।

List splitWithTokens(str, pat) {
    def tokens=[]
    def lastMatch=0
    def m = str=~pat
    while (m.find()) {
      if (m.start() > 0) tokens << str[lastMatch..<m.start()]
      tokens << m.group()
      lastMatch=m.end()
    }
    if (lastMatch < str.length()) tokens << str[lastMatch..<str.length()]
    tokens
}

[['<html><head><title>this is the title</title></head>',/<[^>]+>/],
 ['before<html><head><title>this is the title</title></head>after',/<[^>]+>/]
].each { 
   println splitWithTokens(*it)
}


0

एक अत्यंत भोला और अकुशल समाधान जो फिर भी काम करता है। दो बार स्ट्रिंग पर विभाजित करें और फिर दो सरणियों को मिलाएं

String temp[]=str.split("\\W");
String temp2[]=str.split("\\w||\\s");
int i=0;
for(String string:temp)
System.out.println(string);
String temp3[]=new String[temp.length-1];
for(String string:temp2)
{
        System.out.println(string);
        if((string.equals("")!=true)&&(string.equals("\\s")!=true))
        {
                temp3[i]=string;
                i++;
        }
//      System.out.println(temp.length);
//      System.out.println(temp2.length);
}
System.out.println(temp3.length);
String[] temp4=new String[temp.length+temp3.length];
int j=0;
for(i=0;i<temp.length;i++)
{
        temp4[j]=temp[i];
        j=j+2;
}
j=1;
for(i=0;i<temp3.length;i++)
{
        temp4[j]=temp3[i];
        j+=2;
}
for(String s:temp4)
System.out.println(s);

0
    String expression = "((A+B)*C-D)*E";
    expression = expression.replaceAll("\\+", "~+~");
    expression = expression.replaceAll("\\*", "~*~");
    expression = expression.replaceAll("-", "~-~");
    expression = expression.replaceAll("/+", "~/~");
    expression = expression.replaceAll("\\(", "~(~"); //also you can use [(] instead of \\(
    expression = expression.replaceAll("\\)", "~)~"); //also you can use [)] instead of \\)
    expression = expression.replaceAll("~~", "~");
    if(expression.startsWith("~")) {
        expression = expression.substring(1);
    }

    String[] expressionArray = expression.split("~");
    System.out.println(Arrays.toString(expressionArray));

Scanner scanner = new Scanner("((A+B)*C-D)*E"); scanner.useDelimiter("((?<=[\\+\\*\\-\\/\\(\\)])|(?=[\\+\\*\\-\\/\\(\\)]))"); while (scanner.hasNext()) { System.out.print(" " + scanner.next()); }
रीजेक्सप के

0

इस प्रश्न में सूक्ष्मताओं में से एक "अग्रणी सीमांकक" प्रश्न शामिल है: यदि आप टोकन और सीमांकक का एक संयुक्त सरणी रखने जा रहे हैं, तो आपको यह जानना होगा कि क्या यह टोकन या सीमांकक के साथ शुरू होता है। आप निश्चित रूप से यह मान सकते हैं कि एक अग्रणी परिसीमन को छोड़ दिया जाना चाहिए लेकिन यह एक अनुचित धारणा है। आप यह भी जानना चाह सकते हैं कि आपके पास अनुगामी परिसीमन है या नहीं। यह तदनुसार दो बूलियन झंडे सेट करता है।

ग्रूवी में लिखा गया है लेकिन एक जावा संस्करण काफी स्पष्ट होना चाहिए:

            String tokenRegex = /[\p{L}\p{N}]+/ // a String in Groovy, Unicode alphanumeric
            def finder = phraseForTokenising =~ tokenRegex
            // NB in Groovy the variable 'finder' is then of class java.util.regex.Matcher
            def finderIt = finder.iterator() // extra method added to Matcher by Groovy magic
            int start = 0
            boolean leadingDelim, trailingDelim
            def combinedTokensAndDelims = [] // create an array in Groovy

            while( finderIt.hasNext() )
            {
                def token = finderIt.next()
                int finderStart = finder.start()
                String delim = phraseForTokenising[ start  .. finderStart - 1 ]
                // Groovy: above gets slice of String/array
                if( start == 0 ) leadingDelim = finderStart != 0
                if( start > 0 || leadingDelim ) combinedTokensAndDelims << delim
                combinedTokensAndDelims << token // add element to end of array
                start = finder.end()
            }
            // start == 0 indicates no tokens found
            if( start > 0 ) {
                // finish by seeing whether there is a trailing delim
                trailingDelim = start < phraseForTokenising.length()
                if( trailingDelim ) combinedTokensAndDelims << phraseForTokenising[ start .. -1 ]

                println( "leading delim? $leadingDelim, trailing delim? $trailingDelim, combined array:\n $combinedTokensAndDelims" )

            }

-2

मैं जावा को बहुत अच्छी तरह से नहीं जानता, लेकिन यदि आप एक स्प्लिट विधि नहीं खोज सकते हैं जो ऐसा करता है, तो मेरा सुझाव है कि आप बस अपना खुद का बनाएं।

string[] mySplit(string s,string delimiter)
{
    string[] result = s.Split(delimiter);
    for(int i=0;i<result.Length-1;i++)
    {
        result[i] += delimiter; //this one would add the delimiter to each items end except the last item, 
                    //you can modify it however you want
    }
}
string[] res = mySplit(myString,myDelimiter);

यह बहुत सुंदर नहीं है, लेकिन यह करूँगा।


लेकिन क्या होगा अगर आपके पास एक पंक्ति में कई सीमांकक हैं?
किप

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.