2016-08-03 5 views
2

eine Zeichenfolge wie folgt gegeben:Vereinfachen die Extraktion von bestimmtem String-Muster mit einem mehr if-else und split()

>>> s = "X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct" 

Zuerst habe ich die Zeichenfolge von Unterstrichen geteilt werden soll, das heißt:

>>> s.split('_') 
['X/NOUN/dobj>', 
'hold/VERB/ROOT', 
'<membership/NOUN/dobj', 
'<with/ADP/prep', 
'<Y/PROPN/pobj', 
'>,/PUNCT/punct'] 

Wir nehmen an, dass der Unterstrich ausschließlich als Trennzeichen verwendet wird und nie als Teil der Teilzeichenfolge vorhanden ist, die wir extrahieren möchten.

Dann muss ich zuerst überprüfen, ob jeder dieser "Knoten" in meiner gespaltenen Liste oben beginnt von Enden mit einem '>', '<', dann entfernen Sie es und setzen Sie die entsprechende Klammer als das Ende der Unterliste, so etwas wie:

result = [] 
nodes = s.split('_') 
for node in nodes: 
    if node.endswith('>'): 
     result.append(node[:-1].split('/') + ['>']) 
    elif node.startswith('>'): 
     result.append( node[1:].split('/') + ['>']) 
    elif node.startswith('<'): 
     result.append( node[1:].split('/') + ['<']) 
    elif node.endswith('<'): 
     result.append( node[:-1].split('/') + ['<']) 
    else: 
     result.append( node.split('/') + ['-']) 

Und wenn es nicht der Enden mit einer Winkelhalterung läuft dann anhängen wir - bis zum Ende des sublist.

[out]:

[['X', 'NOUN', 'dobj', '>'], 
['hold', 'VERB', 'ROOT', '-'], 
['membership', 'NOUN', 'dobj', '<'], 
['with', 'ADP', 'prep', '<'], 
['Y', 'PROPN', 'pobj', '<'], 
[',', 'PUNCT', 'punct', '>']] 

die ursprüngliche Eingangskette gegeben, gibt es eine weniger ausführliche Art und Weise auf das Ergebnis zu bekommen? Vielleicht mit Regex und Gruppen?

+0

Ich habe meine Antwort aktualisiert. Ich glaube, ich habe den kürzesten gefunden. Ich benutze Ternär, um Regex seine Arbeit zu tun –

Antwort

3
s = 'X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct' 

def get_sentinal(node): 
    if not node: 
     return '-' 
    # Assuming the node won't contain both '<' and '>' at a same time 
    for index in [0, -1]: 
     if node[index] in '<>': 
      return node[index] 
    return '-' 

results = [ 
    node.strip('<>').split('/') + [get_sentinal(node)] 
    for node in s.split('_') 
] 

print(results) 

Dies macht es deutlich nicht machen kürzer, aber persönlich würde ich denke, es ist ein wenig sauberer irgendwie ist.

1

Ja, obwohl es nicht schön ist:

s = "X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct" 

import re 

out = [] 
for part in s.split('_'): 
    Left, Mid, Right = re.search('^([<>]|)(.*?)([<>]|)$', part).groups() 
    tail = ['-'] if not Left+Right else [Left+Right] 
    out.append(Mid.split('/') + tail) 

print(out) 

Online ausprobieren: https://repl.it/Civg

Es stützt sich auf zwei Dinge:

  1. ein RegexMuster die ()()(), wo die drei Gruppen immer macht Kantengruppen suchen nur nach Zeichen <, > oder nichts ([<>]|), und die mittlere Übereinstimmung everyth ing (nicht gierig) (.*?). Das Ganze ist am Anfang (^) und am Ende ($) der Zeichenfolge verankert, so dass es die gesamte Eingabezeichenfolge konsumiert.
  2. Angenommen, Sie haben nie Winkel an beiden Enden der Zeichenfolge, dann ist die kombinierte Zeichenfolge Left+Right entweder eine leere Zeichenfolge plus das Zeichen am Ende, auf die eine oder andere Weise, oder eine vollständig leere Zeichenfolge, die a angibt Strich ist erforderlich.
+0

Regex schwarze Magie !! – alvas

1

Statt meiner anderen Antwort mit regulären Ausdrücken, können Sie eine Menge von Linien und viel Slicing fallen, wenn Sie wissen, dass string.strip('<>')entweder Charakter von beiden Enden der Zeichenfolge, in einem Zug Streifen wird .

Dieser Code ist etwa in der Mitte zwischen Ihrer ursprünglichen und meine Regex-Antwort in der Zeile, aber ist lesbarer für sie.

s = "X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct" 

result = [] 
for node in s.split('_'): 
    if node.startswith('>') or node.startswith('<'): 
     tail = node[0] 
    elif node.endswith('>') or node.endswith('>'): 
     tail = node[-1] 
    else: 
     tail = '-' 
    result.append(node.strip('<>').split('/') + [tail]) 

print(result) 

Try online: https://repl.it/Civr


Edit: wie viel ausführlich weniger mögen Sie bekommen?

result = [node.strip('<>').split('/') + [(''.join(char for char in node if char in '<>') + '-')[0]] for node in s.split('_')] 
print(result) 

Das ist ganz ordentlich, Sie müssen nicht prüfen, welche Seite die <> eingeschaltet ist, oder ob es dort überhaupt. Ein Schritt strip() S entweder Winkel oder welche Seite es auch ist, der nächste Schritt filtert nur die spitzen Klammern aus der Zeichenfolge (egal welche Seite sie sind) und fügt das Bindestrich-Zeichen hinzu. Dies ist entweder eine Zeichenfolge, die mit einem beliebigen spitzen Winkel von einer Seite oder einem einzelnen Strich beginnt. Nimm Char 0, um den richtigen zu bekommen.

2

verwenden:

import re 
s_split = "X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct".split('_') 
for i, text in enumerate(s_split): 
    Left, Mid, Right = re.search('^([<>]?)(.*?)([<>]?)$', text).groups() 
    s_split[i] = Mid.split('/') + [Left+Right or '-'] 

print s_split 

Ich kann nicht eine mögliche Antwort für eine kürzere finden.

Ternär verwenden, um den Code zu verkürzen. Beispiel: print None or "a" wird gedruckt a. Verwenden Sie auch Regex, um das Auftreten von <> leicht zu analysieren.

+0

Cool, ich wusste nicht, dass es möglich ist, innerhalb einer Liste zu tun =) – alvas

0

Ich habe Regex und Gruppen nicht verwendet, aber es kann Lösung als kürzere Weg sein.

>>> result=[] 
>>> nodes=['X/NOUN/dobj>','hold/VERB/ROOT','<membership/NOUN/dobj', 
'<with/ADP/prep','<Y/PROPN/pobj','>,/PUNCT/punct'] 
>>> for node in nodes: 
... nd=node.replace(">",("/>" if node.endswith(">") else ">/")) 
... nc=nd.replace("<",("/<" if nd.endswith("<") else "</")) 
... result.append(nc.split("/")) 
>>> nres=[inner for outer in result for inner in outer] #nres used to join all result at single array. If you dont need single array you can use result. 
1

Noch kürzere mit einer Liste Verständnis und einige regex Magie:

import re  
s = "X/NOUN/dobj>_hold/VERB/ROOT_<membership/NOUN/dobj_<with/ADP/prep_<Y/PROPN/pobj_>,/PUNCT/punct" 

rx = re.compile(r'([<>])|/') 
items = [list(filter(None, match)) \ 
    for item in s.split('_') \ 
    for match in [rx.split(item)]] 

print(items) 
# [['X', 'NOUN', 'dobj', '>'], ['hold', 'VERB', 'ROOT'], ['<', 'membership', 'NOUN', 'dobj'], ['<', 'with', 'ADP', 'prep'], ['<', 'Y', 'PROPN', 'pobj'], ['>', ',', 'PUNCT', 'punct']] 


Erläuterung: Der Code teilt die items von _, teilt sie wieder mit Hilfe des regulären Ausdrucks rx und filtert am Ende leere Elemente aus.
Sehen Sie eine Demo auf ideone.com.

Verwandte Themen