I'm having a little trouble getting Ruby to match quotes correctly. Suppose I wanted to extract any quoted string followed by an exclamation point. I'd like my regular expression match either single or double quotes. I thought the following might work:
re = /(['"])([^\1]*?)\1!/
md = re.match %{ "this looks like "fun"!" }
md[0] #=> "\"this looks like \"fun\"!"
But as you can see instead of matching <"fun"!> it matched <"this looks like "fun"!>, despite the fact that I've told it to match anything but the character that was used to quote it:
[^\1]*?
It works beautifully if I tell it to match either single quotes or double quotes, but I can't write it to match either in a single regular expression:
re = /"([^"]*?)"!/
md = re.match %{ "this looks like "fun"!" }
md[0] #=> "\"fun\"!"
Or:
re = /'([^']*?)'!/
md = re.match %{ 'this looks like 'fun'!' }
md[0] #=> "'fun'!"
It looks to me like backreferences are not available inside character
classes, because the backslash sequence is interpreted as a character.
So in a character class, \1 is the same as \001, commonly known as
control-A.
On 5/24/06, John W. Long <ng@johnwlong.com> wrote:
Hi,
I'm having a little trouble getting Ruby to match quotes correctly.
Suppose I wanted to extract any quoted string followed by an exclamation
point. I'd like my regular expression match either single or double
quotes. I thought the following might work:
re = /(['"])([^\1]*?)\1!/
md = re.match %{ "this looks like "fun"!" }
md[0] #=> "\"this looks like \"fun\"!"
But as you can see instead of matching <"fun"!> it matched <"this looks
like "fun"!>, despite the fact that I've told it to match anything but
the character that was used to quote it:
[^\1]*?
It works beautifully if I tell it to match either single quotes or
double quotes, but I can't write it to match either in a single regular
expression:
re = /"([^"]*?)"!/
md = re.match %{ "this looks like "fun"!" }
md[0] #=> "\"fun\"!"
Or:
re = /'([^']*?)'!/
md = re.match %{ 'this looks like 'fun'!' }
md[0] #=> "'fun'!"
It looks to me like backreferences are not available inside character
classes, because the backslash sequence is interpreted as a character.
So in a character class, \1 is the same as \001, commonly known as
control-A.
irb(main):001:0> re = /!(['"]).*?\1/
=> /!(['"]).*?\1/
irb(main):002:0> fun = %{ "this looks like "fun"!" }
=> " \"this looks like \"fun\"!\" "
irb(main):003:0> md = re.match fun.reverse
=> #<MatchData:0x2820660>
irb(main):004:0> md[0].reverse
=> "\"fun\"!"
This way, instead of trying to negate a character class, you're just
doing a non-greedy match from the anchored exclamation point - quote
mark combo to the first matching quote mark.
-A
···
On 5/24/06, John W. Long <ng@johnwlong.com> wrote:
That makes perfect sense. So how do I get it to do what I want?
I don't think you can have backreference in character class. In this
case it's fairly easy. This is what I'd do
re = %r{
(?:
'[^']+' |
"[^"]+"
)!
}xi
Basically the same as what you did. But you do not need the
reluctanct quantifiers because the negated char class prevents longer
matches anyway. I'm not sure whether there is a performance
difference.
Kind regards
robert
···
2006/5/24, John W. Long <ng@johnwlong.com>:
John W. Long wrote:
> That makes perfect sense. So how do I get it to do what I want?
I have something that works now:
re = /(?:"[^"]*?"|'[^']*?')!/
md = re.match %{ "this looks like "fun"!" }
md[0] #=> "\"fun\"!"
md = re.match %{ 'this looks like 'fun'!' }
md[0] => "'fun'!"
Still, it makes me wonder if it's possible to do it with back references.
Still, it makes me wonder if it's possible to do it with back references.
I haven't played with Oniguruma yet, but it has named groups - maybe a
named backreference can be used in an Oniguruma character class, as \k
is unambiguous...
irb(main):001:0> re = /!(['"]).*?\1/
=> /!(['"]).*?\1/
irb(main):002:0> fun = %{ "this looks like "fun"!" }
=> " \"this looks like \"fun\"!\" "
irb(main):003:0> md = re.match fun.reverse
=> #<MatchData:0x2820660>
irb(main):004:0> md[0].reverse
=> "\"fun\"!"
This way, instead of trying to negate a character class, you're just
doing a non-greedy match from the anchored exclamation point - quote
mark combo to the first matching quote mark.
I do not know if this is a performance killer (probably not ) but frankly I
do not care, this is one of the most original ideas I have ever seen on this
list.
Just wanted to say this!
Really nice!
Robert
P.S.
It was yours was it not?
R
-A
···
On 5/24/06, A LeDonne <aledonne.listmail@gmail.com> wrote:
On 5/24/06, John W. Long <ng@johnwlong.com> wrote:
--
Deux choses sont infinies : l'univers et la bêtise humaine ; en ce qui
concerne l'univers, je n'en ai pas acquis la certitude absolue.
On 5/24/06, A LeDonne <aledonne.listmail@gmail.com> wrote:
doing a non-greedy match from the anchored exclamation point - quote
mark combo to the first matching quote mark.
I do not know if this is a performance killer (probably not ) but
frankly I
do not care, this is one of the most original ideas I have ever seen on
this
list.
Just wanted to say this!
Really nice!
On 5/24/06, A LeDonne <aledonne.listmail@gmail.com> wrote:
OK, one more thought. Do you necessarily need it in md[0]? If not...
re = /(['"])(?:.*\1)*(.*\1!)/
md = re.match %{ "this looks like "fun"!" }
p md[1]<<md[2] #=> "\"fun\"!"
md = re.match %{ 'this looks like 'fun'!' }
p md[1]<<md[2] #=> "'fun'!"
-A
Better: re = /(['"])(?:.*\1)(.*\1!)/
But the point is to match any quoted expression followed by an exclamation point. The string:
%{ "this looks like "fun"!" }
Is only to demonstrate an expression that I was having trouble greping. Your expression would require a quote and then a quoted expression followed by an exclamation point--not exactly what I was looking for.
But the point is to match any quoted expression followed by an
exclamation point. The string:
%{ "this looks like "fun"!" }
Is only to demonstrate an expression that I was having trouble greping.
Your expression would require a quote and then a quoted expression
followed by an exclamation point--not exactly what I was looking for.
Ignore my "better". what I had the first time,
re = /(['"])(?:.*\1)*(.*\1!)/
was actually correct. That way, it requires zero or more intervening
matching quoty things. That's what I get for not writing unit tests
first...
But the point is to match any quoted expression followed by an
exclamation point. The string:
%{ "this looks like "fun"!" }
Is only to demonstrate an expression that I was having trouble greping.
Your expression would require a quote and then a quoted expression
followed by an exclamation point--not exactly what I was looking for.
John,
I wonder if this also does the job:
re = /['"][\w\s]*\W*['"]!/
md = re.match %{ "this looks like ' a fun test !? '!" }
puts md[0]