Complicated python regex
<p>I have multiple lines like this:</p>

<pre><code>EWSR1{ENST00000397938}:r.1_1364_FLI1{ENST00000429175}:r.1046_3051
EML4{ENST00000318522}:r.1_929_EML4{ENST00000318522}:r.903+188_903+220_ALK{ENST00000389048}:r.4080_6220
FUS{ENST00000254108}:r.1_(608)_FUS{ENST00000254108}:r.(819)_937_DDIT3{ENST00000547303}:r.76_872
TCF3{ENST00000262965}:r.1_1795_ins27_PBX1{ENST00000420696}:r.454_6636
EML4{ENST00000318522}:r.?_ALK{ENST00000389048}:r.?
</code></pre>

<p>I need something like this as output:</p>

<p>For the first one:</p>

<pre><code>EWSR1 ENST00000397938 1 1364
FLI1 ENST00000429175 1046 3051
</code></pre>

<p>For second one:</p>

<pre><code>EML4 ENST00000318522 1 929
EML4 ENST00000318522 903+188 903+220
ALK ENST00000389048 4080 6220
</code></pre>

<p>Third one:</p>

<pre><code>FUS ENST00000254108 1 (608)
FUS ENST00000254108 (819) 937
DDIT3 ENST00000547303 76 872
</code></pre>

<p>4th one:</p>

<pre><code>TCF3 ENST00000262965 1 1795
ins27
PBX1 ENST00000420696 454 6636
</code></pre>

<p>5th one:</p>

<pre><code>EML4 ENST00000318522 ?
ALK ENST00000389048 ?
</code></pre>

<p>I have millions of lines, so i wanted to came up with a regex for this, however i was unable. I created a conditioned regex, but i am sure there is a much more elegant and easier way. Can someone show me a simpler way?</p>

<p>My solution now:</p>

<pre><code>import re
import sys

string = sys.argv[1]
if '+?' in string or '-?' in string or not "?" in string:
    for i in re.findall('\w*?\{.*?\}:r\.[\(\)\?\+\-\d]*_[\(\)\?\+\-\d]*', string):
        if 'ins' in i:
            print(re.findall('ins[A-Za-z0-9]*', i)[0])
            i = re.sub('ins[A-Za-z0-9]*', "", i)
        print(i.lstrip('_').split('{')[0], re.findall('\{(.*?)\}', i.lstrip('_'))[0], " ".join(i.lstrip('_').split('r.')[-1].split('_')))

else:
    for i in re.findall('\w*?\{.*?\}:r\.\?', string):
        if 'ins' in i:
            print(re.findall('ins[A-Za-z0-9]*', i)[0])
            i = re.sub('ins[A-Za-z0-9]*', "", i)
        print(i.lstrip('_').split('{')[0], re.findall('\{(.*?)\}', i.lstrip('_'))[0], " ".join(i.lstrip('_').split('r.')[-1].split('_')))
</code></pre>

<p>Here is a one regex solution, that might not be so elegant, but working:</p>

<pre><code>((?&lt;![^_])ins\d+)_|([a-zA-Z]+[0-9]*)\{([^{}]*)\}:r\.([-()?+\d]+)?(?:_([-()?+\d]+))?
</code></pre>

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

<p><em>Details</em>:</p>

<ul>
<li><code>((?&lt;![^_])ins\d+)_</code> - Group 1 capturing <code>ins</code> (not preceded with a char other than <code>_</code>) with one or more digits after and then <code>_</code> </li>
<li><code>|</code> - or</li>
<li><code>([a-zA-Z]+[0-9]*)</code> - 1+ ASCII letters followed with 0+ digits (if they can be intermingled, use <code>\w*</code> instead of <code>[0-9]*</code>)</li>
<li><code>\{([^{}]*)\}</code> - a <code>{...}</code> substring with the contents (that can have no <code>{</code> nor <code>}</code>) captured into Group 2</li>
<li><code>:r\.</code> -  literal char sequence <code>:r.</code></li>
<li><code>([-()?+\d]+)?</code> - an optional capturing group (ID 3) matching <code>-</code>, <code>(</code>, <code>)</code>, <code>?</code>, <code>+</code>, or a digit 1 or more times greedily</li>
<li><code>(?:_([-()?+\d]+))?</code> - an optional non-capturing group matching <code>_</code> and capturing into Group 4 the same subpattern as above.</li>
</ul>

<p><a href="https://ideone.com/GUDur7" rel="nofollow">Python demo</a>:</p>

<pre><code>import re
regex = r"((?&lt;![^_])ins\d+)_|([a-zA-Z]+[0-9]*)\{([^{}]*)\}:r\.([-()?+\d]+)?(?:_([-()?+\d]+))?"
test = ["EWSR1{ENST00000397938}:r.1_1364_FLI1{ENST00000429175}:r.1046_3051",
    "EML4{ENST00000318522}:r.1_929_EML4{ENST00000318522}:r.903+188_903+220_ALK{ENST00000389048}:r.4080_6220",
    "FUS{ENST00000254108}:r.1_(608)_FUS{ENST00000254108}:r.(819)_937_DDIT3{ENST00000547303}:r.76_872",
    "TCF3{ENST00000262965}:r.1_1795_ins27_PBX1{ENST00000420696}:r.454_6636",
    "EML4{ENST00000318522}:r.?_ALK{ENST00000389048}:r.?"]
res = []
for s in test:
    for match in re.finditer(regex, s):
        tmp = []
        for groupNum in range(0, len(match.groups())):
            if match.group(groupNum+1):
                tmp.append(match.group(groupNum+1))
        res.append(tmp)
print(res)
</code></pre>

<p>Results:</p>

<pre><code>[
 ['EWSR1', 'ENST00000397938', '1', '1364'],
 ['FLI1', 'ENST00000429175', '1046', '3051'],

 ['EML4', 'ENST00000318522', '1', '929'],
 ['EML4', 'ENST00000318522', '903+188', '903+220'],
 ['ALK', 'ENST00000389048', '4080', '6220'],

 ['FUS', 'ENST00000254108', '1', '(608)'],
 ['FUS', 'ENST00000254108', '(819)', '937'],
 ['DDIT3', 'ENST00000547303', '76', '872'],

 ['TCF3', 'ENST00000262965', '1', '1795'],
 ['ins27'],
 ['PBX1', 'ENST00000420696', '454', '6636'],

 ['EML4', 'ENST00000318522', '?'],
 ['ALK', 'ENST00000389048', '?']
]
</code></pre>

python regex python-3.x