Jason T
Jason T

Reputation: 57

Regular Expression to select text between curly braces, Ruby

I'm working on a way to filter and replace broken curly brace tags such as {{hello}. I've tried out a few regular expressions from here in Stack and tried on my own. The closes I've come is using this regex (?=(\}(?!\})))((?<!\})\}) which selects the last tag in the example code block below. However it does not select the entire tag, it just selects the ending curly brace }.

{{hello}}
{{world}}}
{{foobar}}
{{hello}

What I need to do is select any tag that is missing the second ending curly brace like {{hello}. Can anyone help me with the regex to select this type of tag?

Upvotes: 1

Views: 2446

Answers (3)

Wiktor Stribiżew
Wiktor Stribiżew

Reputation: 626806

I suggest using the following expression:

/(?<!{){{[^{}]+}(?!})/

See the regex101 demo

The pattern will match any string of text that starts with {{ not preceded with {, followed with any 1+ characters other than { and } and then a } that is not followed with }. Thus, this pattern matches strings of exactly {{xxx} structure.

Here is a Ruby demo:

"{{hello}".gsub(/(?<!{){{[^{}]+}(?!})/, "\\0}")
# => {{hello}}

Pattern details:

  • (?<!{) - a negative lookbehind failing the match if a { appears immediately to the left of the current position
  • {{ - literal {{
  • [^{}]+ - 1+ characters other than { and } (to allow empty values, use * instead of +)
  • } - a closing single }
  • (?!}) - a negative lookahead failing the match if a } appears right after the previously matched }.

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110675

I assume we are given a string containing substrings beginning with "{{", followed by a "tag", which is a string of characters other than "{" and "}", followed by either "}" or "}}". We wish to return the tags that are followed by only one right brace. For example:

str = "Sue said {{hello}}, Bob said {{world}\nTom said {{foobar}}, Lola said {{hello}"

We can use the following regex:

r = /
    \{\{        # match {{
    ([^}]+)     # match one or more characters other than } in capture group 1
    \}          # match }
    (?:\z|[^}]) # match end of line or a character other than }
                # in a non-capture group        
    /x          # free-spacing regex definition mode

str.scan(r).flatten
  #=> ["world", "hello"]

The regex could of course be written in the conventional way:

r = /\{\{([^}]+)\}(?:\z|[^}])/

Note

str.scan(r)
  => [["world"], ["hello"]] 

hence the need for flatten.

See String#scan for an explanation.

Obviously, the same regex works if

str = "{{hello}}\n{{world}\n{{foobar}}\n{{hello}"

str.scan(r).flatten
  #> ["world", "hello"]

If

words = %w| {{hello}} {{world} {{foobar}} {{hello} |
  #=> ["{{hello}}", "{{world}", "{{foobar}}", "{{hello}"]

then

words.select { |w| w =~ r }.map { |w| w[/[^{}]+/] } 
  => ["world", "hello"] 

Upvotes: 1

Laurel
Laurel

Reputation: 6173

filter and replace broken curly brace tags

This problem is really easy to solve if you're not nesting things.

Try this:

[\{]+([^}]+)[}]+

Essentially, you can just replace the match with {{\1}} (or {{$1}}, I forget which one Ruby uses.)

It will work as long as there are one or more of { and } consecutively around the match.

Upvotes: 1

Related Questions