web-dev-qa-db-fra.com

Faire correspondre les noms, les dialogues et les actions de la transcription à l'aide de regex

Avec un dialogue de chaîne tel que ci-dessous, je dois trouver la phrase qui correspond à chaque utilisateur. 

text = 'CHRIS: Hello, how are you...
PETER: Great, you? PAM: He is resting.
[PAM SHOWS THE COUCH]
[PETER IS NODDING HIS HEAD]
CHRIS: Are you ok?'

Pour le dialogue ci-dessus, je voudrais retourner des tuples avec trois éléments avec: 

1) le nom de la personne

2) la phrase en minuscule et 

3) Les phrases dans les crochets

Quelque chose comme ca: 

('CHRIS', 'Bonjour, comment vas-tu ...', Aucun)

('PETER', 'Super, toi?', Aucun)

('PAM', 'Il se repose', 'PAM MONTRE LE COUCHE. PETER IS APPUIE LA TÊTE')

('CHRIS', 'ça va?', Aucun)

etc 

J'essaie d'utiliser regex pour atteindre ce qui précède. Jusqu'à présent, j'ai pu obtenir les noms des utilisateurs avec les informations ci-dessous. J'ai du mal à identifier la phrase entre deux utilisateurs. 

actors = re.findall(r'\w+(?=\s*:[^/])',text)

Toute aide est très appréciée. 

4
pbou

Vous pouvez le faire avec re.findall:

>>> re.findall(r'\b(\S+):([^:\[\]]+?)\n?(\[[^:]+?\]\n?)?(?=\b\S+:|$)', text)
[('CHRIS', ' Hello, how are you...', ''),
 ('PETER', ' Great, you? ', ''),
 ('PAM',
  ' He is resting.',
  '[PAM SHOWS THE COUCH]\n[PETER IS NODDING HIS HEAD]\n'),
 ('CHRIS', ' Are you ok?', '')]

Vous devrez trouver comment supprimer vous-même les accolades carrées, ce qui ne peut pas être fait avec regex tout en essayant de tout faire correspondre.

Regex Breakdown  

\b              # Word boundary
(\S+)           # First capture group, string of characters not having a space
:               # Colon
(               # Second capture group
    [^          # Match anything that is not...
        :       #     a colon
        \[\]    #     or square braces
    ]+?         # Non-greedy match
)
\n?             # Optional newline
(               # Third capture group
    \[          # Literal opening brace
    [^:]+?      # Similar to above - exclude colon from match
    \] 
    \n?         # Optional newlines
)?              # Third capture group is optional
(?=             # Lookahead for... 
    \b          #     a Word boundary, followed by  
    \S+         #     one or more non-space chars, and
    :           #     a colon
    |           # Or,
    $           # EOL
)
15
coldspeed

Regex est un moyen d’aborder ce problème, mais vous pouvez également le considérer comme une itération de chaque jeton dans votre texte et l’application d’une logique pour former des groupes. 

Par exemple, nous pourrions d’abord trouver des groupes de noms et de texte:

from itertools import groupby

def isName(Word):
    # Names end with ':'
    return Word.endswith(":")

text_split = [
    " ".join(list(g)).rstrip(":") 
    for i, g in groupby(text.replace("]", "] ").split(), isName)
]
print(text_split)
#['CHRIS',
# 'Hello, how are you...',
# 'PETER',
# 'Great, you?',
# 'PAM',
# 'He is resting. [PAM SHOWS THE COUCH] [PETER IS NODDING HIS HEAD]',
# 'CHRIS',
# 'Are you ok?']

Ensuite, vous pouvez collecter des paires d'éléments consécutifs dans text_split en tuples:

print([(text_split[i*2], text_split[i*2+1]) for i in range(len(text_split)//2)])
#[('CHRIS', 'Hello, how are you...'),
# ('PETER', 'Great, you?'),
# ('PAM', 'He is resting. [PAM SHOWS THE COUCH] [PETER IS NODDING HIS HEAD]'),
# ('CHRIS', 'Are you ok?')]

Nous sommes presque à la sortie souhaitée. Nous devons juste traiter le texte entre crochets. Vous pouvez écrire une fonction simple pour cela. (Les expressions régulières sont certes une option ici, mais je l’évite volontairement dans cette réponse.)

Voici quelque chose de rapide que je suis venu avec:

def isClosingBracket(Word):
    return Word.endswith("]")

def processWords(words):
    if "[" not in words:
        return [words, None]
    else:
        return [
            " ".join(g).replace("]", ".") 
            for i, g in groupby(map(str.strip, words.split("[")), isClosingBracket)
        ]

print(
    [(text_split[i*2], *processWords(text_split[i*2+1])) for i in range(len(text_split)//2)]
)
#[('CHRIS', 'Hello, how are you...', None),
# ('PETER', 'Great, you?', None),
# ('PAM', 'He is resting.', 'PAM SHOWS THE COUCH. PETER IS NODDING HIS HEAD.'),
# ('CHRIS', 'Are you ok?', None)]

Notez que l'utilisation de * pour décompresser le résultat de processWords dans Tuple est strictement une fonctionnalité python 3.

1
pault