Python >> Tutoriel Python >  >> Python Tag >> RegEx

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

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, ce qui ne peut pas être fait avec regex tout en essayant de tout faire correspondre.

Répartition des expressions régulières

\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
)

Regex est une façon d'aborder ce problème, mais vous pouvez également y penser en parcourant chaque jeton de votre texte et en appliquant une logique pour former des groupes.

Par exemple, nous pourrions d'abord rechercher 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 n'avons qu'à nous occuper du texte entre crochets. Vous pouvez écrire une fonction simple pour cela. (Les expressions régulières sont certes une option ici, mais j'évite délibérément cela dans cette réponse.)

Voici quelque chose que j'ai trouvé rapidement :

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 du * pour décompresser le résultat de processWords dans le tuple est strictement une fonctionnalité Python 3.