2010-09-26 22 views
13

Je souhaite diviser une phrase en une liste de mots.Comment faire un split() Python sur des langages (comme le chinois) qui n'utilisent pas les espaces comme séparateur de mots?

Pour l'anglais et les langues européennes cela est facile, il suffit d'utiliser fendu()

>>> "This is a sentence.".split() 
['This', 'is', 'a', 'sentence.'] 

Mais je dois aussi faire face à des phrases dans les langues telles que le chinois qui n'utilisent pas d'espace comme séparateur de mots.

>>> u"这是一个句子".split() 
[u'\u8fd9\u662f\u4e00\u4e2a\u53e5\u5b50'] 

De toute évidence, cela ne fonctionne pas. Comment est-ce que je divise une telle phrase dans une liste de mots?

MISE À JOUR:

Jusqu'à présent, les réponses semblent suggérer que cela nécessite des techniques de traitement du langage naturel et que les limites de mot en chinois sont ambigus. Je ne suis pas sûr de comprendre pourquoi. Le mot frontières en chinois me semble très défini. Chaque mot/caractère chinois possède un unicode correspondant et est affiché à l'écran sous la forme d'un mot/caractère distinct.

D'où vient cette ambiguïté? Comme vous pouvez le voir dans ma sortie de la console Python Python n'a pas de problème disant que ma phrase exemple est composé de 5 caractères:

这 - u8fd9 
是 - u662f 
一 - u4e00 
个 - u4e2a 
句 - u53e5 
子 - u5b50 

Alors, évidemment, Python n'a pas de problème indiquant les limites mot/caractère. J'ai juste besoin de ces mots/caractères dans une liste.

+3

Réponse courte: vous n'avez pas. –

+0

Eh bien, qu'est-ce que les Chinois utilisent comme séparateur de mots? –

+4

@Continuation: "mot" n'est pas un mot? Les "limites de caractères" ne posent aucun problème, car tout simplement un caractère Unicode est un caractère chinois. Le plus difficile est de savoir quels caractères chinois appartiennent au même mot. – sth

Répondre

9

juste un mot d'avertissement: en utilisant list('...') (en AP3, c'est u'...' pour AP2) ne pas, au sens général, vous donner les caractères d'une chaîne de caractères Unicode; au contraire, il se traduira très probablement par une série de points de code 16 bits. ceci est vrai pour toutes les constructions CPython 'étroites', ce qui représente la grande majorité des installations python actuelles. Quand unicode a été proposé pour la première fois dans les années 90, il a été suggéré que 16 bits seraient plus que suffisants pour couvrir tous les besoins d'un encodage de texte universel, car il permettait de passer de 128 points de code (7 bits) à 256 points de code. (8 bits) à 65'536 points de code. il devint bientôt évident, cependant, que cela avait été un vœu pieux; Aujourd'hui, environ 100'000 points de code sont définis dans la version 5.2 d'Unicode, et des milliers d'autres sont en attente d'inclusion. pour que cela devienne possible, unicode devait passer de 16 à 32 bits (conceptuellement) (bien qu'il n'utilise pas pleinement l'espace d'adressage 32 bits). Afin de maintenir la compatibilité avec un logiciel construit sur l'hypothèse que l'Unicode était encore 16 bits, on a conçu des paires de substitution, où deux codes de 16 bits provenant de blocs spécifiquement désignés sont utilisés pour exprimer des points de code au-delà de 65'536. est, au-delà de ce qu'unicode appelle le «plan multilingue de base», ou BMP, et qu'on appelle en plaisantant les plans «astral» de cet encodage, pour leur insaisissabilité relative et leurs maux de tête constants qu'ils offrent aux personnes travaillant dans le domaine du traitement de texte et l'encodage. Maintenant que le CPython étroit traite les paires de substitution de façon assez transparente dans certains cas, il ne réussira toujours pas à faire la bonne chose dans d'autres cas, le fractionnement des chaînes étant l'un de ces cas les plus gênants. dans une construction python étroite, list('abc大def') (ou list('abc\u5927\U00027C3Cdef') lorsqu'il est écrit avec des échappements) se traduira par ['a', 'b', 'c', '大', '\ud85f', '\udc3c', 'd', 'e', 'f'], avec '\ud85f', '\udc3c' étant une paire de substitution. incidemment, '\ud85f\udc3c' est ce que le standard JSON attend que vous écriviez pour représenter U-27C3C.l'un ou l'autre de ces points de code est inutile en soi; une chaîne unicode bien formée ne peut avoir que des paires de substituts.

donc ce que vous voulez diviser une chaîne en caractères est vraiment:

from re import compile as _Re 

_unicode_chr_splitter = _Re('(?s)((?:[\ud800-\udbff][\udc00-\udfff])|.)').split 

def split_unicode_chrs(text): 
    return [ chr for chr in _unicode_chr_splitter(text) if chr ] 

qui retourne correctement ['a', 'b', 'c', '大', '', 'd', 'e', 'f'] (note: vous pouvez probablement réécrire l'expression régulière de sorte que le filtrage des chaînes vides devient inutile). Si tout ce que vous voulez faire est de diviser un texte en caractères chinois, vous seriez à peu près fait à ce stade. Je ne suis pas sûr du concept de «mot» de l'OP, mais pour moi, 这 是 一个 句子 peut être divisé en 这 |是 |一 |个 | As子 ainsi que 这 是 |一个 |句子, en fonction de votre point de vue. cependant, tout ce qui dépasse le concept de caractères (éventuellement composés) et de classes de caractères (symboles vs espaces vs lettres et autres) va bien au-delà de ce qui est construit en unicode et en python; vous aurez besoin d'un traitement en langage naturel pour le faire. permettez-moi de remarquer que, bien que votre exemple 'yes the United Nations can!'.split() démontre avec succès que la méthode split fait quelque chose d'utile pour beaucoup de données, elle n'analyse pas correctement le texte anglais: elle ne reconnaît pas United Nations comme un seul mot, alors qu'elle suppose faussement can! un mot, ce qui n'est clairement pas. cette méthode donne à la fois des faux positifs et des faux négatifs. en fonction de vos données et de ce que vous avez l'intention d'accomplir, cela peut être ou ne pas être ce que vous voulez.

+1

Nations Unies est 2 mots, même si c'est 1 nom propre –

15

Vous pouvez le faire, mais pas avec les fonctions de bibliothèque standard. Et les expressions régulières ne vous aideront pas non plus.

La tâche que vous décrivez fait partie du champ Natural Language Processing (NLP). Il y a déjà eu beaucoup de travail sur la division des mots chinois aux frontières des mots. Je suggérerais que vous utilisiez l'une de ces solutions existantes plutôt que d'essayer de rouler la vôtre.

D'où vient l'ambiguïté?

Ce que vous avez énuméré il ya des caractères chinois. Ceux-ci sont à peu près analogues aux lettres ou syllabes en anglais (mais pas tout à fait le même que NullUserException pointe dans un commentaire). Il n'y a pas d'ambiguïté quant à l'emplacement des limites des caractères - c'est très bien défini. Mais vous avez demandé non caractères limites, mais pour mot frontières. Les mots chinois peuvent comporter plus d'un caractère.

Si tout ce que vous voulez est de trouver les caractères, c'est très simple et ne nécessite pas de bibliothèque PNL. Décodez simplement le message en une chaîne unicode (si ce n'est déjà fait) puis convertissez la chaîne unicode en liste en utilisant un appel à la fonction intégrée list. Cela vous donnera une liste des caractères de la chaîne.Pour votre exemple précis:

>>> list(u"这是一个句子") 
+0

En effet. Je pourrais ajouter http://alias-i.com/lingpipe/demos/tutorial/chineseTokens/read-me.html –

+0

La grande majorité des caractères chinois ont une signification individuelle par eux-mêmes. Dans l'exemple: "这 是 一个 句子" chacun de ces caractères signifie quelque chose - "这 = ceci", "是 = est", "一 = un", "个 = a", "句 = phrase". La partie délicate est que certains sont des caractères composés qui signifient une "chose" (par exemple: "句子" = phrase). Parfois aussi, les caractères composés ont une signification complètement différente des caractères individuels. – NullUserException

+2

Donc chacun de ces caractères Unicode est un 字 pour le Chinois, ce qui n'est pas la même chose qu'un "mot" (词), mais qui n'est pas non plus équivalent à une lettre ou une syllabe occidentale. – NullUserException

1

Il est partiellement possible avec le japonais, où vous avez habituellement différentes classes de caractères au début et à la fin du mot, mais les documents scientifiques sont entiers sur le sujet pour le chinois. J'ai une expression régulière pour diviser des mots en japonais si vous êtes intéressé: http://hg.hatta-wiki.org/hatta-dev/file/cd21122e2c63/hatta/search.py#l19

4

Les langues comme le chinois ont une définition très fluide d'un mot. Par exemple. Une signification de ma est "cheval". Une signification de shang est "dessus" ou "sur". Un composé est "mashang" qui signifie littéralement "à cheval" mais est utilisé au sens figuré pour signifier "immédiatement". Vous avez besoin d'un très bon dictionnaire avec des composés et la recherche du dictionnaire nécessite une approche plus longue. La composition est répandue en allemand (exemple célèbre est quelque chose comme "la femme du directeur de compagnie de navigation à vapeur du Danube" s'exprimant en un mot), les langues turques, finnois et magyar - ces langues ont des mots très longs dont beaucoup ne seront pas trouvés un dictionnaire et besoin de décomposer pour les comprendre.

Votre problème est celui de la linguistique, rien à voir avec Python.

+0

Je suppose que nous utilisons simplement le terme «mot» quand nous l'appliquons au chinois. Pour moi "ma shang" ("马上") sont simplement 2 mots/caractères.马 est le 1er, 上 est le second. – Continuation

+3

Pour moi, un mot est une chaîne d'un ou plusieurs caractères qui a une signification particulière. Notez que certains caractères n'ont pas de signification propre et n'ont de sens qu'en conjonction avec un autre personnage. Une liste de caractères n'est pas très utile. –

2

Ok I figured it out.

Ce que je dois peut être accompli en utilisant simplement la liste():

>>> list(u"这是一个句子") 
[u'\u8fd9', u'\u662f', u'\u4e00', u'\u4e2a', u'\u53e5', u'\u5b50'] 

Merci pour toutes vos entrées.

+3

-1 Ce dont vous pensez avoir besoin n'est pas très utile. C'est comme si l'on essayait d'extraire le sens du "petit déjeuner" des deux concepts séparés "pause" et "rapide". –

+1

je dirais laisser à l'OP ce qu'il pense est utile. et le petit déjeuner est, en effet, de «casser» le «jeûne», de manger à nouveau après avoir cessé de le faire pendant un moment. pas utile? – flow

+0

@flow: "petit déjeuner" * était * (pas "est") pour "casser" le "rapide" ... la connexion est aussi ténue que "neuf dragon" -> "Kowloon" –

0

La liste() est la réponse pour la phrase chinoise seulement. Pour ces hybrides anglais/chinois dans la plupart des cas. Il a répondu à hybrid-split, il suffit de copier réponse de Winter comme ci-dessous.

def spliteKeyWord(str): 
    regex = r"[\u4e00-\ufaff]|[0-9]+|[a-zA-Z]+\'*[a-z]*" 
    matches = re.findall(regex, str, re.UNICODE) 
    return matches