How to find a sentence containing a phrase in text using python re?
<p>I have some text which is sentences, some of which are questions. I'm trying to create a regular expression which will extract only the questions which contain a specific phrase, namely 'NSF' :</p>

<pre><code>import re
s = "This is a string. Is this a question? This isn't a question about NSF. Is this one about NSF? This one is a question about NSF but is it longer?"
</code></pre>

<p>Ideally, the re.findall would return:</p>

<pre><code>['Is this one about NSF?','This one is a question about NSF but is it longer?']
</code></pre>

<p>but my current best attempt is:</p>

<pre><code>re.findall('([\.\?].*?NSF.*\?)+?',s)
[". Is this a question? This isn't a question about NSF. Is this one about NSF? This one is a question about NSF but is it longer?"]
</code></pre>

<p>I know I need to do something with non-greedy-ness, but I'm not sure where I'm messing up.</p>

<p><em>DISCLAIMER</em>: The answer is not aiming at a generic interrogative sentence splitting solution, rather show how the strings supplied by OP can be matched with regular expressions. The best solution is to tokenize the text into sentences with <a href="http://www.nltk.org/" rel="nofollow"><code>nltk</code></a> and parse sentences (see <a href="http://stackoverflow.com/questions/17879551/nltk-find-if-a-sentence-is-in-a-questioning-form">this thread</a>).</p>

<p>The regex you might want to use for strings like the one you posted is based on matching all chars that are not final punctuation and then matching the subtring you want to appear inside the sentence, and then matching those chars other than final punctuation again. To negated a single character, use negated character classes.</p>

<pre><code>\s*([^!.?]*?NSF[^!.?]*?[?])
</code></pre>

<p>See the <a href="https://regex101.com/r/LXr6x8/3" rel="nofollow">regex demo</a>.</p>

<p><strong>Details</strong>:</p>

<ul>
<li><code>\s*</code> - 0+ whitespaces</li>
<li><code>([^!.?]*?NSF[^.?]*?[?])</code>  - Group 1 capturing

<ul>
<li><code>[^!.?]*?</code> - 0+ chars other than <code>.</code>, <code>!</code> and <code>?</code>, as few as possible</li>
<li><code>NSF</code> - the value you need to be present, a sequence of chars <code>NSF</code></li>
<li><code>[^.?]*?</code> -  ibid.</li>
<li><code>[?]</code> -  a literal <code>?</code> (can be replaced with <code>\?</code>)</li>
</ul></li>
</ul>

python regex non-greedy