4

I'm trying to get the values between {} and %% in a same Regex. This is what I have till now. I can successfully get values individually for each but I was curious to learn about how can I combine both.

var regex = new Regex(@"%(.*?)%|\{([^}]*)\}");

String s = "This is a {test} %String%. %Stack% {Overflow}";

Expected answer for the above string

test
String
Stack
Overflow

Individual regex

@"%(.*?)%" gives me String and Stack
@"\{([^}]*)\}" gives me test and Overflow

Following is my code.

var regex = new Regex(@"%(.*?)%|\{([^}]*)\}");
var matches = regex.Matches(s); 
foreach (Match match in matches) 
{
    Console.WriteLine(match.Groups[1].Value);
}
  • Your regex is just fine you need to use match.Captures[0].Value not match.Groups[1].Value – Craig Selbert Feb 24 '16 at 22:42
  • http://stackoverflow.com/questions/3320823/whats-the-difference-between-groups-and-captures-in-net-regular-expression – Craig Selbert Feb 24 '16 at 22:44
  • Didn't see there is [conditional](http://www.regular-expressions.info/conditional.html) answer already. You can also try [`(?:({)|%)(.*?)(?(1)}|%)`](http://regexstorm.net/tester?p=(%3F%3A(%7B)%7C%25)(.*%3F)(%3F(1)%7D%7C%25)&i=This%20is%20a%20%7Btest%7D%20%25String%25.%20%25Stack%25%20%7BOverflow%7D) – bobble bubble Feb 24 '16 at 23:29

5 Answers5

3

Similar to your regex. You can use Named Capturing Groups

String s = "This is a {test} %String%. %Stack% {Overflow}";
var list = Regex.Matches(s, @"\{(?<name>.+?)\}|%(?<name>.+?)%")
           .Cast<Match>()
           .Select(m => m.Groups["name"].Value)
           .ToList();
Eser
  • 12,346
  • 1
  • 22
  • 32
  • @WiktorStribiżew Sorry, I don't get what you are saying. This code returns what OP wants. See `Expected answer for the above string ` and compare with what this code returns.... – Eser Feb 24 '16 at 22:59
  • You should have explained you are using groups with identical names, and describe the feature. – Wiktor Stribiżew Feb 24 '16 at 23:02
  • @WiktorStribiżew Seems like "what you dislike in this answer" has changed. No I won't explain anything. This is my way.... And I think this is more readable than your regex... – Eser Feb 24 '16 at 23:04
  • @WiktorStribiżew thanks for -1, but I won't do the same childish thing for you. – Eser Feb 24 '16 at 23:14
  • I do not think code only answers deserve ANY upvotes. SO is a place for those who want to learn. Do not give fish, teach to fish. You openly say you don't want to share your knowledge with others - that is why I downvoted. If you downvote my answer, that would be childish, yes. – Wiktor Stribiżew Feb 24 '16 at 23:15
  • @WiktorStribiżew It is just your opinion..... If you want learn how to use it, You can use the link in my answer :) – Eser Feb 24 '16 at 23:15
2

If you want to learn how conditional expressions work, here is a solution using that kind of .NET regex capability:

(?:(?<p>%)|(?<b>{))(?<v>.*?)(?(p)%|})

See the regex demo

Here is how it works:

  • (?:(?<p>%)|(?<b>{)) - match and capture either Group "p" with % (percentage), or Group "b" (brace) with {
  • (?<v>.*?) - match and capture into Group "v" (value) any character (even a newline since I will be using RegexOptions.Singleline) zero or more times, but as few as possible (lazy matching with *? quantifier)
  • (?(p)%|}) - a conditional expression meaning: if "p" group was matched, match %, else, match }.

enter image description here

C# demo:

var s = "This is a {test} %String%. %Stack% {Overflow}";
var regex = "(?:(?<p>%)|(?<b>{))(?<v>.*?)(?(p)%|})";
var matches = Regex.Matches(s, regex, RegexOptions.Singleline); 
// var matches_list = Regex.Matches(s, regex, RegexOptions.Singleline)
//                 .Cast<Match>() 
//                 .Select(p => p.Groups["v"].Value)
//                 .ToList(); 
// Or just a demo writeline
foreach (Match match in matches) 
    Console.WriteLine(match.Groups["v"].Value);
Wiktor Stribiżew
  • 607,720
  • 39
  • 448
  • 563
1

Sometimes the capture is in group 1 and sometimes it's in group 2 because you have two pairs of parentheses.

Your original code will work if you do this instead:

Console.WriteLine(match.Groups[1].Value + match.Groups[2].Value);

because one group will be the empty string and the other will be the value you're interested in.

Matthew Strawbridge
  • 19,940
  • 10
  • 72
  • 93
0
@"[\{|%](.*?)[\}|%]"

The idea being:

{ or %
anything
} or %
Derek
  • 7,615
  • 5
  • 33
  • 58
0

I think you should use a combination of conditional anda nested groups:

((\{(.*)\})|(%(.*)%))
tede24
  • 2,304
  • 11
  • 14