क्या मैं जावा रेगेक्स में समूहों को बदल सकता हूं?


95

मेरे पास यह कोड है, और मैं जानना चाहता हूं, अगर मैं जावा रेगेक्स में केवल समूहों (सभी पैटर्न नहीं) को बदल सकता हूं। कोड:

 //...
 Pattern p = Pattern.compile("(\\d).*(\\d)");
    String input = "6 example input 4";
    Matcher m = p.matcher(input);
    if (m.find()) {

        //Now I want replace group one ( (\\d) ) with number 
       //and group two (too (\\d) ) with 1, but I don't know how.

    }

6
क्या आप अपने प्रश्न को स्पष्ट कर सकते हैं, जैसे कि उस इनपुट के लिए अपेक्षित आउटपुट दे सकते हैं?
माइकल मायर्स

जवाबों:


125

का उपयोग करें $n(जहां n एक अंक है) पर कब्जा कर लिया अनुवर्ती में संदर्भित करने के लिए replaceFirst(...)। मैं मान रहा हूं कि आप पहले समूह को शाब्दिक स्ट्रिंग "संख्या" और दूसरे समूह को पहले समूह के मान से बदलना चाहते थे ।

Pattern p = Pattern.compile("(\\d)(.*)(\\d)");
String input = "6 example input 4";
Matcher m = p.matcher(input);
if (m.find()) {
    // replace first number with "number" and second number with the first
    String output = m.replaceFirst("number $3$1");  // number 46
}

के (\D+)बजाय दूसरे समूह के लिए विचार करें (.*)*एक लालची मिलानकर्ता है, और सबसे पहले अंतिम अंक का उपभोग करेगा। मैचर को तब पीछे हटना होगा जब यह पता चलेगा कि फाइनल के (\d)पास मैच के लिए कुछ भी नहीं है, इससे पहले कि वह अंतिम अंक से मेल खा सके।


7
अच्छा होता अगर आप एक उदाहरण आउटपुट
winklerrr

6
यह पहले मैच पर काम करता है, लेकिन अगर कई समूह हैं और आप काम नहीं कर रहे हैं, तो आप उनके साथ थोड़ी देर (m.find) ()
ह्यूगो ज़रागोज़ा

1
मैं ह्यूगो के साथ सहमत हूं, यह समाधान को लागू करने का एक भयानक तरीका है ... पृथ्वी पर यह क्यों स्वीकार किया गया जवाब है और न कि acdcjunior का जवाब - जो सही समाधान है: कोड की छोटी राशि, उच्च सामंजस्य और कम युग्मन, बहुत कम मौका (यदि कोई मौका नहीं है) अवांछित साइड इफेक्ट्स ... आह ...
फायरलाइट

यह उत्तर वर्तमान में मान्य नहीं है। m.replaceFirst("number $2$1");होना चाहिएm.replaceFirst("number $3$1");
डैनियल Eisenreich

52

आप एक सामान्य प्रतिस्थापन विधि का उपयोग Matcher#start(group)और Matcher#end(group)निर्माण कर सकते हैं :

public static String replaceGroup(String regex, String source, int groupToReplace, String replacement) {
    return replaceGroup(regex, source, groupToReplace, 1, replacement);
}

public static String replaceGroup(String regex, String source, int groupToReplace, int groupOccurrence, String replacement) {
    Matcher m = Pattern.compile(regex).matcher(source);
    for (int i = 0; i < groupOccurrence; i++)
        if (!m.find()) return source; // pattern not met, may also throw an exception here
    return new StringBuilder(source).replace(m.start(groupToReplace), m.end(groupToReplace), replacement).toString();
}

public static void main(String[] args) {
    // replace with "%" what was matched by group 1 
    // input: aaa123ccc
    // output: %123ccc
    System.out.println(replaceGroup("([a-z]+)([0-9]+)([a-z]+)", "aaa123ccc", 1, "%"));

    // replace with "!!!" what was matched the 4th time by the group 2
    // input: a1b2c3d4e5
    // output: a1b2c3d!!!e5
    System.out.println(replaceGroup("([a-z])(\\d)", "a1b2c3d4e5", 2, 4, "!!!"));
}

चेक यहां ऑनलाइन डेमो


1
यह वास्तव में स्वीकृत उत्तर होना चाहिए और साथ में कोडिंग के स्तर को पेश किए बिना यह सबसे पूर्ण और "जाने के लिए तैयार" समाधान है। हालाँकि मैं उनमें से किसी एक के नाम को बदलने की सलाह दूंगा। पहली नज़र में यह पहली विधि में एक पुनरावर्ती कॉल की तरह दिखता है।
फायरलाइट

अवसर संपादित संपादित किया। पुनरावर्ती कॉल के बारे में भाग वापस लें, कोड का ठीक से विश्लेषण नहीं किया। ओवरलोड एक साथ अच्छी तरह से काम करते हैं
फायरलाइट

23

एक मरे हुए घोड़े को पीटने के लिए क्षमा करें, लेकिन यह अजीब तरह का है कि किसी ने भी यह नहीं कहा - "हां आप कर सकते हैं, लेकिन यह विपरीत है कि आप वास्तविक जीवन में समूहों को कैसे उपयोग करते हैं"।

यदि आप रेगेक्स का उपयोग करने के तरीके से करते हैं, तो समाधान इस प्रकार सरल है:

"6 example input 4".replaceAll("(?:\\d)(.*)(?:\\d)", "number$11");

या जैसा कि नीचे shmosel द्वारा बताया गया है,

"6 example input 4".replaceAll("\d(.*)\d", "number$11");

... चूंकि आपके रेगेक्स में दशमलव को समूहीकृत करने का कोई अच्छा कारण नहीं है।

आप आम तौर पर स्ट्रिंग के उन हिस्सों पर कब्जा करने वाले समूहों का उपयोग नहीं करते हैं जिन्हें आप छोड़ना चाहते हैं , आप उन्हें उस स्ट्रिंग के हिस्से पर उपयोग करते हैं जिसे आप रखना चाहते हैं

यदि आप वास्तव में उन समूहों को चाहते हैं जिन्हें आप बदलना चाहते हैं, तो आप शायद इसके बजाय एक अस्थायी इंजन चाहते हैं (जैसे मूंछें, ईजेएस, स्ट्रिंगमप्लेट, ...)।


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

(?:abc)*(capture me)(?:bcd)*

आपको उनकी ज़रूरत है अगर आपका इनपुट "abcabc कैप्चर मी bcdbcd" या "abc कैप्चर मी एलसीडी " या यहाँ तक कि " मुझ पर कब्जा " की तरह दिख सकता है ।

या इसे दूसरे तरीके से रखने के लिए: यदि पाठ हमेशा समान होता है, और आप इसे कैप्चर नहीं करते हैं, तो समूहों का उपयोग करने का कोई कारण नहीं है।


1
गैर-कैप्चरिंग समूह अनावश्यक हैं; \d(.*)\dपर्याप्त होगा।
shmosel

1
मुझे $11यहाँ समझ नहीं आ रहा है । 11 क्यों?
एलेक्सिस

1
@Alexis - यह एक जावा regex मोड़: अगर समूह 11 सेट, जावा व्याख्या $ 11 के रूप में $ 1 से 1. का पालन नहीं किया गया है
Yaro

9

पार्न्स को जोड़कर एक तीसरा समूह जोड़ें .*, फिर उसके बाद की जगह लें "number" + m.group(2) + "1"। उदाहरण के लिए:

String output = m.replaceFirst("number" + m.group(2) + "1");

4
दरअसल, मैचर $ 2 संदर्भ शैली का समर्थन करता है, इसलिए m.replaceFirst ("संख्या $ 21") एक ही काम करेगा।
माइकल मायर्स

दरअसल, वे ऐसा नहीं करते हैं। "number$21"काम करता है और "number" + m.group(2) + "1"नहीं करता है।
एलन मूर

2
ऐसा लगता है number$21कि समूह 21 की जगह लेगा, न कि समूह 2 + स्ट्रिंग "1"।
फर्नांडो एम। पिनहिरो

यह सादा स्ट्रिंग संघनन है, है ना? हमें रिप्लेसमेंट को कॉल करने की आवश्यकता क्यों है?
Zxcv Mnb

2

समूह की स्थिति प्राप्त करने के लिए आप matcher.start () और matcher.end () विधियों का उपयोग कर सकते हैं। इसलिए इस पदों का उपयोग करके आप किसी भी पाठ को आसानी से बदल सकते हैं।


1

इनपुट से पासवर्ड फ़ील्ड बदलें:

{"_csrf":["9d90c85f-ac73-4b15-ad08-ebaa3fa4a005"],"originPassword":["uaas"],"newPassword":["uaas"],"confirmPassword":["uaas"]}



  private static final Pattern PATTERN = Pattern.compile(".*?password.*?\":\\[\"(.*?)\"\\](,\"|}$)", Pattern.CASE_INSENSITIVE);

  private static String replacePassword(String input, String replacement) {
    Matcher m = PATTERN.matcher(input);
    StringBuffer sb = new StringBuffer();
    while (m.find()) {
      Matcher m2 = PATTERN.matcher(m.group(0));
      if (m2.find()) {
        StringBuilder stringBuilder = new StringBuilder(m2.group(0));
        String result = stringBuilder.replace(m2.start(1), m2.end(1), replacement).toString();
        m.appendReplacement(sb, result);
      }
    }
    m.appendTail(sb);
    return sb.toString();
  }

  @Test
  public void test1() {
    String input = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"123\"],\"newPassword\":[\"456\"],\"confirmPassword\":[\"456\"]}";
    String expected = "{\"_csrf\":[\"9d90c85f-ac73-4b15-ad08-ebaa3fa4a005\"],\"originPassword\":[\"**\"],\"newPassword\":[\"**\"],\"confirmPassword\":[\"**\"]}";
    Assert.assertEquals(expected, replacePassword(input, "**"));
  }

0

यहां एक अलग समाधान है, जो कई मैचों में एकल समूह के प्रतिस्थापन की अनुमति देता है। यह निष्पादन आदेश को उलटने के लिए ढेर का उपयोग करता है, इसलिए स्ट्रिंग ऑपरेशन को सुरक्षित रूप से निष्पादित किया जा सकता है।

private static void demo () {

    final String sourceString = "hello world!";

    final String regex = "(hello) (world)(!)";
    final Pattern pattern = Pattern.compile(regex);

    String result = replaceTextOfMatchGroup(sourceString, pattern, 2, world -> world.toUpperCase());
    System.out.println(result);  // output: hello WORLD!
}

public static String replaceTextOfMatchGroup(String sourceString, Pattern pattern, int groupToReplace, Function<String,String> replaceStrategy) {
    Stack<Integer> startPositions = new Stack<>();
    Stack<Integer> endPositions = new Stack<>();
    Matcher matcher = pattern.matcher(sourceString);

    while (matcher.find()) {
        startPositions.push(matcher.start(groupToReplace));
        endPositions.push(matcher.end(groupToReplace));
    }
    StringBuilder sb = new StringBuilder(sourceString);
    while (! startPositions.isEmpty()) {
        int start = startPositions.pop();
        int end = endPositions.pop();
        if (start >= 0 && end >= 0) {
            sb.replace(start, end, replaceStrategy.apply(sourceString.substring(start, end)));
        }
    }
    return sb.toString();       
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.