web-dev-qa-db-fra.com

Python - RegEx pour fractionner du texte en phrases

Je veux faire une liste de phrases d'une chaîne, puis les imprimer. Je ne veux pas utiliser NLTK pour faire cela. Il faut donc diviser un point à la fin de la phrase et non des décimales, des abréviations ou le titre d'un nom ou si la phrase comporte un .com. C'est une tentative de regex qui ne fonctionne pas.

import re

text = """\
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't.
"""
sentences = re.split(r' *[\.\?!][\'"\)\]]* *', text)

for stuff in sentences:
        print(stuff)    

Exemple de sortie de ce à quoi il devrait ressembler

Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. 
Did he mind?
Adam Jones Jr. thinks he didn't.
In any case, this isn't true...
Well, with a probability of .9 it isn't.
17
user3590149
(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)\s

Essaye ça. divisez votre chaîne ceci.Vous pouvez également vérifier la démo.

http://regex101.com/r/nG1gU7/27

24
vks

Essayez de scinder l'entrée en fonction des espaces plutôt que d'un point ou de ?. Si vous procédez ainsi, le point ou ? ne sera pas imprimé dans le résultat final.

>>> import re
>>> s = """Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't."""
>>> m = re.split(r'(?<=[^A-Z].[.?]) +(?=[A-Z])', s)
>>> for i in m:
...     print i
... 
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it.
Did he mind?
Adam Jones Jr. thinks he didn't.
In any case, this isn't true...
Well, with a probability of .9 it isn't.
4
Avinash Raj
sent = re.split('(?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)(\s|[A-Z].*)',text)
for s in sent:
    print s

Ici la regex utilisée est: (?<!\w\.\w.)(?<![A-Z][a-z]\.)(?<=\.|\?)(\s|[A-Z].*)

Premier bloc: (?<!\w\.\w.): ce modèle recherche dans une boucle de rétroaction négative (?<!) tous les mots (\w) suivi de fullstop (\.), suivi des autres mots (\.)

Deuxième bloc: (?<![A-Z][a-z]\.): ce modèle recherche dans une boucle de rétroaction négative tout ce qui commence par un alphabet majuscule ([A-Z]), suivi d'un alphabet minuscule ([a-z]) jusqu'à ce qu'un point (\.) soit trouvé.

Troisième bloc: (?<=\.|\?): ce modèle recherche dans une boucle de rétroaction de point (\.) OR point d'interrogation (\?)

Quatrième bloc: (\s|[A-Z].*): ce modèle cherche après le point d'interrogation OR du troisième bloc. Il recherche un espace vide (\s) OR toute séquence de caractères commençant par un alphabet majuscule ([A-Z].*). Ce bloc est important à scinder si l'entrée est identique à 

Bonjour tout le monde. Salut, je suis ici aujourd'hui.

c'est-à-dire s'il y a de l'espace ou pas du tout après le point.

2
Mehul Gupta

Essaye ça:

(?<!\b(?:[A-Z][a-z]|\d|[i.e]))\.(?!\b(?:com|\d+)\b)
0
walid toumi

Mon exemple est basé sur l'exemple de ALi, adapté au portugais brésilien. Merci ALi.

ABREVIACOES = ['sra?s?', 'exm[ao]s?', 'ns?', 'nos?', 'doc', 'ac', 'publ', 'ex', 'lv', 'vlr?', 'vls?',
               'exmo(a)', 'ilmo(a)', 'av', 'of', 'min', 'livr?', 'co?ls?', 'univ', 'resp', 'cli', 'lb',
               'dra?s?', '[a-z]+r\(as?\)', 'ed', 'pa?g', 'cod', 'prof', 'op', 'plan', 'edf?', 'func', 'ch',
               'arts?', 'artigs?', 'artg', 'pars?', 'rel', 'tel', 'res', '[a-z]', 'vls?', 'gab', 'bel',
               'ilm[oa]', 'parc', 'proc', 'adv', 'vols?', 'cels?', 'pp', 'ex[ao]', 'eg', 'pl', 'ref',
               '[0-9]+', 'reg', 'f[ilí]s?', 'inc', 'par', 'alin', 'fts', 'publ?', 'ex', 'v. em', 'v.rev']

ABREVIACOES_RGX = re.compile(r'(?:{})\.\s*$'.format('|\s'.join(ABREVIACOES)), re.IGNORECASE)

        def sentencas(texto, min_len=5):
            # baseado em https://stackoverflow.com/questions/25735644/python-regex-for-splitting-text-into-sentences-sentence-tokenizing
            texto = re.sub(r'\s\s+', ' ', texto)
            EndPunctuation = re.compile(r'([\.\?\!]\s+)')
            # print(NonEndings)
            parts = EndPunctuation.split(texto)
            sentencas = []
            sentence = []
            for part in parts:
                txt_sent = ''.join(sentence)
                q_len = len(txt_sent)
                if len(part) and len(sentence) and q_len >= min_len and \
                        EndPunctuation.match(sentence[-1]) and \
                        not ABREVIACOES_RGX.search(txt_sent):
                    sentencas.append(txt_sent)
                    sentence = []

                if len(part):
                    sentence.append(part)
            if sentence:
                sentencas.append(''.join(sentence))
            return sentencas

Code complet en: https://github.com/luizanisio/comparador_elastic

0
Luiz Anísio

Approche naïve pour les phrases en anglais correctes ne commençant pas par des caractères non alphas et ne contenant pas de parties du discours citées:

import re
text = """\
Mr. Smith bought cheapsite.com for 1.5 million dollars, i.e. he paid a lot for it. Did he mind? Adam Jones Jr. thinks he didn't. In any case, this isn't true... Well, with a probability of .9 it isn't.
"""
EndPunctuation = re.compile(r'([\.\?\!]\s+)')
NonEndings = re.compile(r'(?:Mrs?|Jr|i\.e)\.\s*$')
parts = EndPunctuation.split(text)
sentence = []
for part in parts:
  if len(part) and len(sentence) and EndPunctuation.match(sentence[-1]) and not NonEndings.search(''.join(sentence)):
    print(''.join(sentence))
    sentence = []
  if len(part):
    sentence.append(part)
if len(sentence):
  print(''.join(sentence))

Le dédoublement des faux positifs peut être réduit en allongeant légèrement NonEndings. D'autres cas nécessiteront un code supplémentaire. Gérer les fautes de frappe de manière judicieuse s’avérera difficile avec cette approche.

Vous n'atteindrez jamais la perfection avec cette approche. Mais en fonction de la tâche, cela pourrait fonctionner "assez" ...

0
Ali

J'ai écrit ceci en tenant compte des commentaires de smci ci-dessus. C'est une approche intermédiaire qui ne nécessite pas de bibliothèques externes et n'utilise pas de regex. Il vous permet de fournir une liste d’abréviations et de comptes pour les phrases terminées par des caractères de fin de chaîne, tels que les points et les guillemets: [. ",? ',.)].

abbreviations = {'dr.': 'doctor', 'mr.': 'mister', 'bro.': 'brother', 'bro': 'brother', 'mrs.': 'mistress', 'ms.': 'miss', 'jr.': 'junior', 'sr.': 'senior', 'i.e.': 'for example', 'e.g.': 'for example', 'vs.': 'versus'}
terminators = ['.', '!', '?']
wrappers = ['"', "'", ')', ']', '}']


def find_sentences(paragraph):
   end = True
   sentences = []
   while end > -1:
       end = find_sentence_end(paragraph)
       if end > -1:
           sentences.append(paragraph[end:].strip())
           paragraph = paragraph[:end]
   sentences.append(paragraph)
   sentences.reverse()
   return sentences


def find_sentence_end(paragraph):
    [possible_endings, contraction_locations] = [[], []]
    contractions = abbreviations.keys()
    sentence_terminators = terminators + [terminator + wrapper for wrapper in wrappers for terminator in terminators]
    for sentence_terminator in sentence_terminators:
        t_indices = list(find_all(paragraph, sentence_terminator))
        possible_endings.extend(([] if not len(t_indices) else [[i, len(sentence_terminator)] for i in t_indices]))
    for contraction in contractions:
        c_indices = list(find_all(paragraph, contraction))
        contraction_locations.extend(([] if not len(c_indices) else [i + len(contraction) for i in c_indices]))
    possible_endings = [pe for pe in possible_endings if pe[0] + pe[1] not in contraction_locations]
    if len(paragraph) in [pe[0] + pe[1] for pe in possible_endings]:
        max_end_start = max([pe[0] for pe in possible_endings])
        possible_endings = [pe for pe in possible_endings if pe[0] != max_end_start]
    possible_endings = [pe[0] + pe[1] for pe in possible_endings if sum(pe) > len(paragraph) or (sum(pe) < len(paragraph) and paragraph[sum(pe)] == ' ')]
    end = (-1 if not len(possible_endings) else max(possible_endings))
    return end


def find_all(a_str, sub):
    start = 0
    while True:
        start = a_str.find(sub, start)
        if start == -1:
            return
        yield start
        start += len(sub)

J'ai utilisé la fonction find_all de Karl à partir de cette entrée: Trouver toutes les occurrences d'une sous-chaîne en Python

0
TennisVisuals

Je ne suis pas doué pour les expressions régulières, mais une version plus simple, "force brute" en fait, 

sentence = re.compile("([\'\"][A-Z]|([A-Z][a-z]*\. )|[A-Z])(([a-z]*\.[a-z]*\.)|([A-Za-z0-9]*\.[A-Za-z0-9])|([A-Z][a-z]*\. [A-Za-z]*)|[^\.?]|[A-Za-z])*[\.?]")

ce qui signifie début des unités acceptables sont '[A-Z] ou "[A-Z]
S'il vous plaît noter, la plupart des expressions régulières sont gourmandes donc l'ordre est très important lorsque nous faisons | (ou). C’est la raison pour laquelle j’ai écrit c’est-à-dire expression régulière en premier lieu, puis viennent des formes telles que Inc.  

0
Priyank Pathak