CamExCase या TitleCase (उन्नत) विभाजित करने के लिए RegEx


81

मुझे कैमलकेस या टाइटलकैस अभिव्यक्ति का हिस्सा निकालने के लिए एक शानदार RegEx मिला ।

 (?<!^)(?=[A-Z])

यह उम्मीद के मुताबिक काम करता है:

  • मूल्य -> ​​मूल्य
  • camelValue -> ऊंट / मूल्य
  • TitleValue -> शीर्षक / मूल्य

जावा के साथ उदाहरण के लिए:

String s = "loremIpsum";
words = s.split("(?<!^)(?=[A-Z])");
//words equals words = new String[]{"lorem","Ipsum"}

मेरी समस्या यह है कि यह कुछ मामलों में काम नहीं करता है:

  • केस 1: मान -> वी / ए / एल / यू / ई
  • केस 2: eclipseRCPExt -> ग्रहण / आर / सी / पी / एक्सट

मेरे दिमाग में, परिणाम शाद हो:

  • केस 1: VALUE
  • केस 2: ग्रहण / आरसीपी / एक्सट

दूसरे शब्दों में, n अपरकेस वर्ण दिए गए हैं:

  • यदि n वर्णों को निम्न केस वर्णों द्वारा अनुसरण किया जाता है, तो समूह होने चाहिए: (n-1 वर्ण) / (n-th char + निचला वर्ण)
  • यदि n वर्ण अंत में हैं, तो समूह होना चाहिए: (n char)।

कैसे इस regex में सुधार करने पर कोई विचार?


ऐसा लगता है कि आपको संभवतः ^नकारात्मक लुकहैंड में कैपिटल अक्षरों के लिए एक और सशर्त मामले में सशर्त संशोधक की आवश्यकता होगी । यकीन के लिए परीक्षण नहीं किया गया है, लेकिन मुझे लगता है कि समस्या को ठीक करने के लिए आपका सबसे अच्छा शर्त होगा।
रात्रि ०

यदि कोई परीक्षा दे रहा है
क्लैम

जवाबों:


112

उपरोक्त सभी उदाहरणों के लिए निम्नलिखित रेगेक्स काम करता है:

public static void main(String[] args)
{
    for (String w : "camelValue".split("(?<!(^|[A-Z]))(?=[A-Z])|(?<!^)(?=[A-Z][a-z])")) {
        System.out.println(w);
    }
}   

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

"RPC" और "Ext" के बीच विभाजन करने में विफल रहने से "स्वयं ग्रहण" पर रेगेक्स का पहला भाग विफल हो जाता है। यह दूसरे खंड का उद्देश्य है (?<!^)(?=[A-Z][a-z]:। यह खंड स्ट्रिंग की शुरुआत को छोड़कर, प्रत्येक कैपिटल लेटर से पहले एक स्प्लिट लेटर के बाद एक विभाजन की अनुमति देता है।


1
यह एक PHP पर काम नहीं करता है, जबकि @ राइडररनर करता है। PHP में यह कहा गया है कि "दिखावे की भरपाई 13 की लंबाई पर निश्चित नहीं है"।
igorsantos07

15
@ इगोरु: रेगेक्स का स्वाद अलग-अलग होता है। सवाल जावा के बारे में है, न कि PHP के बारे में, और इसलिए इसका जवाब है।
NPE

1
जबकि प्रश्न को "जावा" के रूप में चिह्नित किया गया है, प्रश्न अभी भी जेनेरिक है - कोड नमूनों के अलावा (जो कभी भी सामान्य नहीं हो सकता)। तो, अगर वहाँ इस regex का एक सरल संस्करण है और यह भी क्रॉस-भाषा में काम करता है, मैंने सोचा कि किसी को यह इंगित करना चाहिए कि :)
igorsantos07

7
@ इगोरू: "जेनेरिक रेगेक्स" एक काल्पनिक अवधारणा है।
कासिमिर एट हिप्पोलीटे

3
@ igorsantos07: नहीं, बिल्ट-इन regex कार्यान्वयन प्लेटफ़ॉर्म के बीच बेतहाशा भिन्न होते हैं। कुछ पर्ल-जैसे होने की कोशिश कर रहे हैं, कुछ POSIX की तरह बनने की कोशिश कर रहे हैं, और कुछ बीच में या पूरी तरह से अलग हैं।
क्रिस्टोफर हैमरस्टॉर्मम

78

ऐसा लगता है कि आप इसे ज़रूरत से ज़्यादा जटिल बना रहे हैं। के लिए CamelCase , विभाजन स्थान बस कहीं भी एक अपरकेस अक्षर तुरंत एक छोटा अक्षर इस प्रकार है:

(?<=[a-z])(?=[A-Z])

यहां बताया गया है कि यह रेगेक्स आपके उदाहरण डेटा को कैसे विभाजित करता है:

  • value -> value
  • camelValue -> camel / Value
  • TitleValue -> Title / Value
  • VALUE -> VALUE
  • eclipseRCPExt -> eclipse / RCPExt

आपके वांछित आउटपुट से एकमात्र अंतर यह है eclipseRCPExtकि मैं जो तर्क देता हूं वह सही ढंग से यहां विभाजित है।

परिशिष्ट - बेहतर संस्करण

नोट: इस उत्तर को हाल ही में एक उत्थान मिला और मुझे एहसास हुआ कि एक बेहतर तरीका है ...

उपरोक्त रेगेक्स में एक दूसरा विकल्प जोड़कर, ओपी के सभी परीक्षण मामलों को सही ढंग से विभाजित किया गया है।

(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])

यहां बताया गया है कि बेहतर रेगेक्स उदाहरण डेटा को कैसे विभाजित करता है:

  • value -> value
  • camelValue -> camel / Value
  • TitleValue -> Title / Value
  • VALUE -> VALUE
  • eclipseRCPExt -> eclipse / RCP / Ext

संपादित करें: 20130824RCPExt -> RCP / Ext मामले को संभालने के लिए बेहतर संस्करण जोड़ा गया ।


आपके सहयोग के लिए धन्यवाद। मुझे इस उदाहरण में आरसीपी और एक्सट को अलग करने की आवश्यकता है, क्योंकि मैं भागों को एक स्थिर नाम (स्टाइल गाइडलाइन: "सभी अपरकेस को अलग-अलग शब्दों का उपयोग करके परिवर्तित करता हूं।") इस मामले में, मैं ECLIPSE_RCP_EXT को ECLIPSE_RCPEXT पसंद करता हूं।
जैमिनि

4
सहायता के लिए धन्यवाद; मैंने आपके (?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?<=[0-9])(?=[A-Z][a-z])|(?<=[a-zA-Z])(?=[0-9])
रीगेक्स

यह सबसे अच्छा जवाब है! सरल और स्पष्ट। हालाँकि यह उत्तर और ओपी द्वारा मूल RegEx जावास्क्रिप्ट और गोलंग के लिए काम नहीं करता है!
वियतनाम


10

मुझे काम करने के लिए Aix का समाधान नहीं मिल सका (और यह RegExr पर भी काम नहीं करता है), इसलिए मैं अपने साथ आया हूं कि मैंने परीक्षण किया है और ऐसा लगता है कि आप क्या देख रहे हैं:

((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))

और यहाँ इसका उपयोग करने का एक उदाहरण है:

; Regex Breakdown:  This will match against each word in Camel and Pascal case strings, while properly handling acrynoms.
;   (^[a-z]+)                       Match against any lower-case letters at the start of the string.
;   ([A-Z]{1}[a-z]+)                Match against Title case words (one upper case followed by lower case letters).
;   ([A-Z]+(?=([A-Z][a-z])|($)))    Match against multiple consecutive upper-case letters, leaving the last upper case letter out the match if it is followed by lower case letters, and including it if it's followed by the end of the string.
newString := RegExReplace(oldCamelOrPascalString, "((^[a-z]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($))))", "$1 ")
newString := Trim(newString)

यहां मैं प्रत्येक शब्द को एक स्थान के साथ अलग कर रहा हूं, इसलिए यहां कुछ उदाहरण दिए गए हैं कि स्ट्रिंग कैसे रूपांतरित होती है:

  • ThisIsATitleCASEString => यह शीर्षक शीर्षक स्ट्रिंग है
  • andThisOneIsCamelCASE => और यह एक ऊंट मामला है

ऊपर यह समाधान करता है कि मूल पोस्ट क्या मांगता है, लेकिन मुझे ऊंट और पास्कल स्ट्रिंग्स को खोजने के लिए एक rexx की भी आवश्यकता थी जिसमें संख्याएं शामिल थीं, इसलिए मैं भी संख्याओं को शामिल करने के लिए इस भिन्नता के साथ आया था:

((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))

और इसका उपयोग करने का एक उदाहरण:

; Regex Breakdown:  This will match against each word in Camel and Pascal case strings, while properly handling acrynoms and including numbers.
;   (^[a-z]+)                               Match against any lower-case letters at the start of the command.
;   ([0-9]+)                                Match against one or more consecutive numbers (anywhere in the string, including at the start).
;   ([A-Z]{1}[a-z]+)                        Match against Title case words (one upper case followed by lower case letters).
;   ([A-Z]+(?=([A-Z][a-z])|($)|([0-9])))    Match against multiple consecutive upper-case letters, leaving the last upper case letter out the match if it is followed by lower case letters, and including it if it's followed by the end of the string or a number.
newString := RegExReplace(oldCamelOrPascalString, "((^[a-z]+)|([0-9]+)|([A-Z]{1}[a-z]+)|([A-Z]+(?=([A-Z][a-z])|($)|([0-9]))))", "$1 ")
newString := Trim(newString)

और यहां कुछ उदाहरण दिए गए हैं कि कैसे संख्याओं के साथ एक स्ट्रिंग इस रेगेक्स में बदल जाती है:

  • myVariable123 => मेरा परिवर्तनीय 123
  • my2Variables => मेरे 2 वेरिएबल्स
  • The3rdVariableIsHere => 3 rdVariable यहाँ है
  • 12345NumsAtTheStartIncludedToo => 12345 Nums प्रारंभ में बहुत शामिल हैं

1
बहुत सारे अनावश्यक कैप्चरिंग समूह। आप इसे इस प्रकार लिख सकते हैं: (^[a-z]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|$))पहले एक के लिए, और (^[a-z]+|[0-9]+|[A-Z][a-z]+|[A-Z]+(?=[A-Z][a-z]|$|[0-9]))दूसरे के लिए। सबसे बाहरी को भी हटाया जा सकता है, लेकिन पूरे मैच को संदर्भित करने के लिए वाक्यविन्यास भाषाओं के बीच पोर्टेबल नहीं है ( $0और $&2 संभावनाएं हैं)।
न्हादत्त

उसी सरलीकृत regexp:([A-Z]?[a-z]+)|([A-Z]+(?=[A-Z][a-z]))
एलेक्स Suhinin

3

बस से अधिक अक्षरों को संभालने के लिए A-Z:

s.split("(?<=\\p{Ll})(?=\\p{Lu})|(?<=\\p{L})(?=\\p{Lu}\\p{Ll})");

या तो:

  • किसी भी लोअरकेस अक्षर के बाद विभाजित करें, उसके बाद अपरकेस अक्षर है।

उदाहरण के लिए parseXML-> parse, XML

या

  • किसी भी पत्र के बाद विभाजित करें, इसके बाद ऊपरी मामले पत्र और लोअरकेस पत्र है।

उदाहरण के लिए XMLParser-> XML, Parser


अधिक पठनीय रूप में:

public class SplitCamelCaseTest {

    static String BETWEEN_LOWER_AND_UPPER = "(?<=\\p{Ll})(?=\\p{Lu})";
    static String BEFORE_UPPER_AND_LOWER = "(?<=\\p{L})(?=\\p{Lu}\\p{Ll})";

    static Pattern SPLIT_CAMEL_CASE = Pattern.compile(
        BETWEEN_LOWER_AND_UPPER +"|"+ BEFORE_UPPER_AND_LOWER
    );

    public static String splitCamelCase(String s) {
        return SPLIT_CAMEL_CASE.splitAsStream(s)
                        .collect(joining(" "));
    }

    @Test
    public void testSplitCamelCase() {
        assertEquals("Camel Case", splitCamelCase("CamelCase"));
        assertEquals("lorem Ipsum", splitCamelCase("loremIpsum"));
        assertEquals("XML Parser", splitCamelCase("XMLParser"));
        assertEquals("eclipse RCP Ext", splitCamelCase("eclipseRCPExt"));
        assertEquals("VALUE", splitCamelCase("VALUE"));
    }    
}

3

संक्षिप्त करें

दोनों शीर्ष उत्तर सकारात्मक लुकबाइंड का उपयोग करके कोड प्रदान करते हैं, जो सभी रेगेक्स फ्लेवर द्वारा समर्थित नहीं है। नीचे regex दोनों पर कब्जा होगा PascalCaseऔर camelCaseऔर कई भाषाओं में इस्तेमाल किया जा सकता।

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

कोड

इस रेगेक्स को यहां देखें

([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)

परिणाम

नमूना इनपुट

eclipseRCPExt

SomethingIsWrittenHere

TEXTIsWrittenHERE

VALUE

loremIpsum

नमूना आउटपुट

eclipse
RCP
Ext

Something
Is
Written
Here

TEXT
Is
Written
HERE

VALUE

lorem
Ipsum

व्याख्या

  • मैच एक या अधिक अपरकेस अल्फा वर्ण [A-Z]+
  • या शून्य या एक अपरकेस अल्फा चरित्र से मेल खाते हैं [A-Z]?, उसके बाद एक या अधिक लोअरकेस अल्फा वर्ण होते हैं[a-z]+
  • सुनिश्चित करें कि निम्न प्रकार एक बड़ा अक्षर वर्ण [A-Z]या शब्द सीमा वर्ण है\b


0

आप जावा के लिए नीचे दिए गए अभिव्यक्ति का उपयोग कर सकते हैं:

(?<=[a-z])(?=[A-Z])|(?<=[A-Z])(?=[A-Z][a-z])|(?=[A-Z][a-z])|(?<=\\d)(?=\\D)|(?=\\d)(?<=\\D)

3
हाय Maicon, StackOverflow में आपका स्वागत है और आपके उत्तर के लिए धन्यवाद। हालांकि यह प्रश्न का उत्तर दे सकता है, लेकिन यह दूसरों को यह जानने के लिए कोई स्पष्टीकरण नहीं देता है कि यह समस्या का समाधान कैसे करता है। क्या आप अपने कोड का स्पष्टीकरण शामिल करने के लिए अपना उत्तर संपादित कर सकते हैं? धन्यवाद!
टिम मेलोन

0

विभाजकों की तलाश करने के बजाय जो आप वहां नहीं हैं, आप नाम घटकों को खोजने पर भी विचार कर सकते हैं (जो निश्चित रूप से वहां हैं):

String test = "_eclipse福福RCPExt";

Pattern componentPattern = Pattern.compile("_? (\\p{Upper}?\\p{Lower}+ | (?:\\p{Upper}(?!\\p{Lower}))+ \\p{Digit}*)", Pattern.COMMENTS);

Matcher componentMatcher = componentPattern.matcher(test);
List<String> components = new LinkedList<>();
int endOfLastMatch = 0;
while (componentMatcher.find()) {
    // matches should be consecutive
    if (componentMatcher.start() != endOfLastMatch) {
        // do something horrible if you don't want garbage in between

        // we're lenient though, any Chinese characters are lucky and get through as group
        String startOrInBetween = test.substring(endOfLastMatch, componentMatcher.start());
        components.add(startOrInBetween);
    }
    components.add(componentMatcher.group(1));
    endOfLastMatch = componentMatcher.end();
}

if (endOfLastMatch != test.length()) {
    String end = test.substring(endOfLastMatch, componentMatcher.start());
    components.add(end);
}

System.out.println(components);

यह आउटपुट [eclipse, 福福, RCP, Ext]। एक सरणी में रूपांतरण निश्चित रूप से सरल है।


0

मैं पुष्टि कर सकता हूं कि रेगेक्स स्ट्रिंग ([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b) ctwheels द्वारा दिया गया , रेगेक्स के Microsoft स्वाद के साथ काम करता है।

मैं निम्नलिखित विकल्प का सुझाव देना चाहूंगा, जो ctwheels 'regex पर आधारित है, जो संख्यात्मक वर्णों को संभालता है: ([A-Z0-9]+|[A-Z]?[a-z]+)(?=[A-Z0-9]|\b) :।

यह तार को विभाजित करने में सक्षम है जैसे:

ड्राइविंगबी 2 बीट्रेडइएन 2019ऑनवर्ड

सेवा

2019 में बी 2 बी व्यापार ड्राइविंग


0

एक जावास्क्रिप्ट समाधान

/**
 * howToDoThis ===> ["", "how", "To", "Do", "This"]
 * @param word word to be split
 */
export const splitCamelCaseWords = (word: string) => {
    if (typeof word !== 'string') return [];
    return word.replace(/([A-Z]+|[A-Z]?[a-z]+)(?=[A-Z]|\b)/g, '!$&').split('!');
};

वे एक जावास्क्रिप्ट समाधान के लिए पूछते हैं। और आप एक ही समाधान को दो बार क्यों दे रहे हैं ? अगर आपको लगता है कि वे प्रश्न इंडेंटिकल हैं, तो एक को डुप्लिकेट के रूप में बंद करें।
टोटो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.