%s showing strange behavior in regex
<p>I have a string in which I want to find some words preceding a parenthesis. Lets say the string is - </p>

<blockquote>
  <p>'there are many people in the world having colorectal cancer (crc) who also have the depression syndrome (ds)'</p>
</blockquote>

<p>I want to capture at most 5 words before a parenthesis. I have a list <code>acronym_list</code> of abbreviations which are inside the brackets - <code>[(crc), (ds)]</code>. So I am using the following code - </p>

<pre><code>acrolen=5
rt=[]
for acro in acronym_list:
    find_words= re.findall('((?:\w+\W+){1,%d}%s)'  %(acrolen, acro), text, re.I)
    for word in find_words:
            rt.append(word)
print rt
</code></pre>

<p>But this gives this result - </p>

<pre><code>('the world having colorectal cancer (crc', 'crc')
('also have the depression syndrome (ds', 'ds')
</code></pre>

<p>Whereas if I use the regex -</p>

<p><code>find_words= re.findall('((?:\w+\W+){1,%d}\(crc\))'  %(acrolen),s, re.I)</code></p>

<p>Then it is able to find exactly what I want i.e. - </p>

<pre><code>the world having colorectal cancer (crc)
</code></pre>

<p>The question is - why using <code>%s</code> for the string here causing the regex match to be so vastly different (having unnecessary brackets around it, repeating the acronym etc..)</p>

<p>How can I use the 1st regex properly so that I can automate the process using a loop rather than having to enter the exact string every time in the regex ?</p>

<p>You need to make sure the variables you pass are escaped correctly so as to be used as literal text inside a regex pattern. Use <code>re.escape(acro)</code>:</p>

<pre><code>import re
text = "there are many people in the world having colorectal cancer (crc) who also have the depression syndrome (ds)"
acrolen=5
rt=[]
acronym_list = ["(crc)", "(ds)"]
for acro in acronym_list:
    p = r'((?:\w+\W+){1,%d}%s)' %(acrolen, re.escape(acro))
    # Or, use format:
    # p = r'((?:\w+\W+){{1,{0}}}{1})'.format(acrolen, re.escape(acro))
    find_words= re.findall(p, text, re.I)
    for word in find_words:
        rt.append(word)
print rt
</code></pre>

<p>See the <a href="https://ideone.com/9lwLYm" rel="nofollow">Python demo</a></p>

<p>Also, note you do not need to enclose the whole pattern with a capturing group, <code>re.findall</code> will return match values if no capturing group is defined in the pattern.</p>

<p>It is also recommended to use raw string literals when defining regex patterns to avoid ambiguous situations.</p>

python regex