views:

83

answers:

5

Suppose there are two strings:

$1 off delicious ham.
$1 off delicious $5 ham.

In Python, can I have a regex that matches when there is only one $ in the string? I.e., I want the RE to match on the first phrase, but not on the second. I tried something like:

re.search(r"\$[0-9]+.*!(\$)","$1 off delicious $5 ham.")

..saying "Match where you see a $ followed by anything EXCEPT for another $." There was no match on the $$ example, but there was also no match on the $ example.

Thanks in advance!

Simple test method for checking:

def test(r):
  s = ("$1 off $5 delicious ham","$1 off any delicious ham")    
  for x in s:
    print x
    print re.search(r,x,re.I)
    print ""
+6  A: 
>>> import re
>>> onedollar = re.compile(r'^[^\$]*\$[^\$]*$')
>>> onedollar.match('$1 off delicious ham.')
<_sre.SRE_Match object at 0x7fe253c9c4a8>
>>> onedollar.match('$1 off delicious $5 ham.')
>>>

Breakdown of regexp:
^ Anchor at start of string
[^\$]* Zero or more characters that are not $
\$ Match a dollar sign
[^\$]* Zero or more characters that are not $
$ Anchor at end of string

MattH
Bonus points for the show-n-tell. Do you have a couple minutes to describe to me what's going on, here?
Chris
0 or more non dollars sign followed by one dollar followed by 0 or more non dollar sign
Xavier Combelle
Very helpful indeed; thanks.
Chris
You don't need to escape the `$` inside a character class. `r'^[^$]*\$[^$]*$'` works just as well.
Tim Pietzcker
A: 
re.search("^[^$]*\$[^$]*$",test_string)
gnibbler
+1  A: 
^.*?\$[^$]*$

this should make the trick

dweeves
nice use of a non-greedy match
ar
Hmm.. Didn't pass the test, though, unless there are some special flags I need:<pre> >>> test(r"^.*?\$[^$]*$") $1 off $5 delicious ham <_sre.SRE_Match object at 0x00ED6288> $1 off any delicious ham <_sre.SRE_Match object at 0x00ED6288> </pre>
Chris
+2  A: 

You want to use the complement of a character class [^] to match any character other than $:

re.match(r"\$[0-9]+[^\$]*$","$1 off delicious $5 ham.")

The changes from your original are as follows:

  1. .* replaced with [^\$]*. The new term [^\$] means any character other than $
  2. $ appended to string. Forces the match to extend to the end of the string.
  3. re.search replaced with re.match. Matches the whole string, rather than any subset of it.
fmark
Didn't pass!>>> test(r"\$[0-9]+[^\$]*$")$1 off $5 delicious ham<_sre.SRE_Match object at 0x00ED65D0>$1 off any delicious ham<_sre.SRE_Match object at 0x00ED65D0>
Chris
+3  A: 
>>> '$1 off delicious $5 ham.'.count('$')
2
>>> '$1 off delicious ham.'.count('$')
1
SilentGhost
I agree, a regular expression would be overkill here.
Duncan