Craig Walker
Craig Walker

Reputation: 51727

Matching an optional substring in a regex

I'm developing an algorithm to parse a number out of a series of short-ish strings. These strings are somewhat regular, but there's a few different general forms and several exceptions. I'm trying to build a set of regexes that will handle the various forms and exceptions; I'll apply them one after another to see if I get a match.

One of these forms goes something like this:

X (Y) Z

Where:

Also: Y is optional; it doesn't always appear in a string with Z and X. So, I want to be able to extract the numbers from all of these strings:

Right now, I have a regex that will capture the first one:

([0-9]+) +Z

My problem is that I don't know how to construct a regex that will match a series of characters if and only if they're enclosed in parenthesis. Can this be done in a single regex?

Upvotes: 72

Views: 215693

Answers (4)

Godeke
Godeke

Reputation: 16281

(\d+)\s+(\(.*?\))?\s?Z

Note the escaped parentheses, and the ? (zero or once) quantifiers. Any of the groups you don't want to capture can be (?: non-capture groups).

I agree about the spaces. \s is a better option there. I also changed the quantifier to insure there are digits at the beginning. As far as newlines, that would depend on context: if the file is parsed line by line it won't be a problem. Another option is to anchor the start and end of the line (add a ^ at the front and a $ at the end).

Upvotes: 86

Martin Kool
Martin Kool

Reputation: 4245

This ought to work:

^\d+\s?(\([^\)]+\)\s?)?Z$

Haven't tested it though, but let me give you the breakdown, so if there are any bugs left they should be pretty straightforward to find:

First the beginning:

^ = beginning of string
\d+ = one or more decimal characters
\s? = one optional whitespace

Then this part:

(\([^\)]+\)\s?)?

Is actually:

(.............)?

Which makes the following contents optional, only if it exists fully

\([^\)]+\)\s?

\( = an opening bracket
[^\)]+ = a series of at least one character that is not a closing bracket
\) = followed by a closing bracket
\s? = followed by one optional whitespace

And the end is made up of

Z$

Where

Z = your constant string
$ = the end of the string

Upvotes: 23

Konrad Rudolph
Konrad Rudolph

Reputation: 545588

You can do this:

([0-9]+) (\([^)]+\))? Z

This will not work with nested parens for Y, however. Nesting requires recursion which isn't strictly regular any more (but context-free). Modern regexp engines can still handle it, albeit with some difficulties (back-references).

Upvotes: 10

Kip
Kip

Reputation: 109413

Try this:

X (\(Y\))? Z

Upvotes: 7

Related Questions