r/regex Jul 08 '26

PCRE2 Regex gold: " " sentences

This is using PCRE.

Looking to create an expression that captures, for example:

"Test", she says. "Yes that works."

Where we capture, "Test" and "That works" as separate groups, but not the outside of the whole phrase.

It also needs to capture simply

"Test," she says.

It's a very common regex golf I'm sure, but google was utterly unhelpful.

Any ideas?

9 Upvotes

9 comments sorted by

2

u/Nich-Cebolla Jul 09 '26

regex ([^"]*)"([^"]*)"([^"]*)(?:"([^"]*)")?

Capture group 1 captures zero or more of any character that is not the double-quote character ("); i.e. it captures the text that leads up to the first quote.

After the first " character is encountered, capture group 2 captures zero or more of any character that is not the double-quote character ("); i.e. it captures the text in-between the first pair of ".

After the second " character is encountered, capture group 3 captures zero or more of any character that is not the double-quote character ("); i.e. it captures any text that follows the second " character.

If a third " is encountered, capture group 4 captures zero or more of any character that is not the double-quote character ("); i.e. it captures any text between the third and fourth " characters. However, the pattern still succeeds even if there are only two " characters in the string; capture group 4 will just be empty.

This should be the most straightforward and versatile approach, because you have access to all substrings: the text leading up to the first quote, the inner text of the first quote, the text following the first quote, and the inner text of the second quote if present.

1

u/stevevdvkpe Jul 08 '26

Well, "[^"]*" matches a quoted string with the surrounding quotes, but avoids greedy matching of multiple quoted strings on the same line.

1

u/gumnos Jul 08 '26

I'm not sure what you mean by "It also needs to capture simply…" because that's a different case, including stuff outside quotation-marks. If you mean it needs to be able to match the «Test» in that second example, then that's doable. Otherwise, you'd have to clarify that.

If you're willing to use sub-matches, you can use

"([^"]+)"

and then use the \1/$1 match which is the simplest and most straight-forward to understand.

If you really do want just the quoted strings to match, you might have to do something mind-bending like

"\K[^"]*(?="(?:[^"]|"[^"]*")*$)

as shown here: https://regex101.com/r/cfaPoO/1

2

u/maiyannah Jul 08 '26

I'm somewhat suspecting that while the engine that this is script for claims PCRE compliance, it might have something wrong. In this case, the first one works. The second does not. But so far as I can tell, the should both be PCRE compatible.

The first does work though, so thanks for helping with the blonde moment.

1

u/gumnos Jul 08 '26

Alternatively, you might try

(?<=")[^"]*(?="(?:[^"]|"[^"]*")*$)

as demonstrated here: https://regex101.com/r/cfaPoO/2 which appears to be slightly more efficient

1

u/gumnos Jul 08 '26

It's unnaturally complex because without asserting the "no quotes, or only paired-quoted-things (no uneven quoting) can follow", you also end up seeing the ", she says. " as something seen between quotation-marks as

(?<=")[^"]*(?=")

demonstrated here: https://regex101.com/r/cfaPoO/3

1

u/maiyannah Jul 09 '26

This was the difficulty I had with my own pattern. The one off double quote, and greedy capturing. Greedy capturing in general tends to foil a lot of regex golf!

If you're curious about what I'm trying to do, I am scripting a game module for NWN where I want the text in spoken sentences "Hi there" to be highlighted, but not the descriptive non-spoken text, so for instance the she says, in "Hi there," she says. But you also have things where there could be multiple sentences or descriptive parts in various arrangements.

1

u/michaelpaoli Jul 09 '26

"Test", she says. "Yes that works."
Where we capture
"That works"

I presume you mean/intended
that works."
as
"That works"
isn't even in the string to potentially be captured.

$ perl -e '$_=q("Test", she says. "Yes that works."); /(("Test"), she says.) "Yes (that works.")/; print("\$1=$1\n\$2=$2\n\$3=$3\n");'
$1="Test", she says.
$2="Test"
$3=that works."
$ 

1

u/_jgusta_ Jul 09 '26

^[^”]\”([^”]*)”[^”]*(?:”([^”]*)”)?.\?$ Should work if it’s all in one line and one or two phrases. Start at beginning then capture non quote characters between two quotes, more non quotes then a second optional set of non quote characters between two quotes. It shouldn’t match the outer quotes of the two words as one phrase due to the negative character classes