0

I have this string:

john(man,24,engineer),smith(man,23),lucy(female) 

How do I replace a comma which not in the parentheses with @?

The result should be:

john(man,24,engineer)@smith(man,23)@lucy(female)

My code:

String str = "john(man,24,engineer),smith(man,23),lucy(female)";
Pattern p = Pattern.compile(".*?(?:\\(.*?\\)).+?");
Matcher m = p.matcher(str);
System.out.println(m.matches()+"  "+m.find());

Why is m.matches() true and m.find() false? How can I achieve this?

Amal Murali
  • 75,622
  • 18
  • 128
  • 150
Yinbin Wang
  • 113
  • 6

2 Answers2

4

Use a negative lookahead to achieve this:

,(?![^()]*\))

Explanation:

,         # Match a literal ','
(?!       # Start of negative lookahead
  [^()]*  # Match any character except '(' & ')', zero or more times
  \)      # Followed by a literal ')'
)         # End of lookahead

Regex101 Demo

Amal Murali
  • 75,622
  • 18
  • 128
  • 150
  • 1
    I like that approach (+1), FYI posted complementary approach for situations with unbalanced parentheses such `smiley:)` etc. :) – zx81 Jun 13 '14 at 04:27
3

A simple regex for another approach in case we encounter unbalanced parentheses as insmiley:) or escape\)

While the lookahead approach works (and I too am a fan), it breaks down with input such as ,smiley:)(man,23), so I'll give you an alternative simple regex just in case. For the record, it's hard to find an simple approach that works all of the time because of potential nesting.

This situation is very similar to this question about "regex-matching a pattern unless...". We can solve it with a beautifully-simple regex:

\([^()]*\)|(,)

Of course we can avoid more unpleasantness by allowing the parentheses matched on the left to roll over escaped parentheses:

\((?:\\[()]|[^()])*\)|(,)

The left side of the alternation | matches complete (parentheses). We will ignore these matches. The right side matches and captures commas to Group 1, and we know they are the right commas because they were not matched by the expression on the left.

This program shows how to use the regex (see the results at the bottom of the online demo):

import java.util.*;
import java.io.*;
import java.util.regex.*;
import java.util.List;

class Program {
public static void main (String[] args) throws java.lang.Exception  {

String subject = "john(man,24,engineer),smith(man,23),smiley:)(notaperson) ";
Pattern regex = Pattern.compile("\\([^()]*\\)|(,)");
Matcher m = regex.matcher(subject);
StringBuffer b= new StringBuffer();
while (m.find()) {
    if(m.group(1) != null) m.appendReplacement(b, "@");
    else m.appendReplacement(b, m.group(0));
}
m.appendTail(b);
String replaced = b.toString();
System.out.println(replaced);
} // end main
} // end Program

For more information about the technique

How to match (or replace) a pattern except in situations s1, s2, s3...

Community
  • 1
  • 1
zx81
  • 41,100
  • 9
  • 89
  • 105