如何在不使用空格作为单词分隔符的语言(如中文)上进行Python split()?

时间:2010-09-26 12:21:58

标签: python string unicode nlp cjk

我想将一个句子分成一个单词列表。

对于英语和欧洲语言,这很简单,只需使用split()

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

但我还需要处理不使用空格作为单词分隔符的中文等语言的句子。

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

显然这不起作用。

如何将这样的句子分成单词列表?

更新

到目前为止,答案似乎表明这需要自然语言处理技术,并且中文中的单词边界是模糊的。我不确定我明白为什么。中文中的单词边界对我来说非常明确。每个中文单词/字符都有一个相应的unicode,并在屏幕上显示为单独的单词/字符。

那么模糊性来自何处。正如您在我的Python控制台输出中所看到的,Python没有问题,我的例句由5个字符组成:

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

很明显,Python在讲述单词/字符边界方面没有问题。我只需要列表中的那些单词/字符。

9 个答案:

答案 0 :(得分:18)

您可以执行此操作,但不能使用标准库函数。正则表达式也无济于事。

您描述的任务是名为Natural Language Processing(NLP)的字段的一部分。在词边界上分割中文单词已经做了很多工作。我建议您使用这些现有解决方案之一,而不是尝试自己的解决方案。

  

模糊性来自何处?

你列出的是汉字。这些与英语中的字母或音节大致相似(但与评论中指出的NullUserException不完全相同)。关于角色边界的位置没有任何歧义 - 这是非常明确的。但是你没有要求字符边界,而是要求边界。中文单词可以包含多个字符。

如果你想要的只是找到字符,那么这很简单,不需要NLP库。只需将消息解码为unicode字符串(如果尚未完成),然后使用对内置函数list的调用将unicode字符串转换为列表。这将为您提供字符串中的字符列表。对于您的具体示例:

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

答案 1 :(得分:9)

请注意:使用list( '...' )(在Py3中;对于Py2来说是u'...' not ,在一般意义上,为您提供字符的unicode字符串;相反,它很可能会导致一系列16位代码点。对于所有'狭窄'的CPython构建来说都是如此,这些构建占今天绝大多数python安装。

当unicode在20世纪90年代首次提出时,有人建议16位足以满足通用文本编码的所有需求,因为它可以从128个代码点(7位)和256个代码点( 8位)到高达65'536的代码点。然而,很快就发现这是一厢情愿的想法;今天,unicode版本5.2中定义了大约100'000个代码点,还有数千个代码点待定。为了使其成为可能,unicode必须从16位移到(概念上)32位(尽管它没有充分利用32位地址空间)。

为了保持与基于unicode仍然是16位的假设的软件的兼容性,设计了所谓的代理对,其中来自特定指定块的两个16位代码点用于表示超过65'536的代码点,即除了unicode所谓的“基本多语言平面”或BMP之外,它们被戏称为编码的“星界”平面,因为它们为文本处理领域的工作人员提供了相对难以捉摸和持续的头痛。编码

现在虽然狭窄的CPython在某些情况下非常透明地处理代理对,但在其他情况下仍然无法做正确的事情,字符串拆分是其中一个更麻烦的情况。在一个狭窄的python构建中,list( 'abc大def' )(或使用转义编写时为list( 'abc\u5927\U00027C3Cdef' ))将导致['a', 'b', 'c', '大', '\ud85f', '\udc3c', 'd', 'e', 'f'],其中'\ud85f', '\udc3c'是代理对。顺便说一句,'\ud85f\udc3c'是JSON标准期望您编写以表示U-27C3C的内容。这些代码点中的任何一个都是无用的;一个结构良好的unicode字符串只能有一对代理。

所以你想把字符串拆分成字符真的是:

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 ]

正确返回['a', 'b', 'c', '大', '', 'd', 'e', 'f'](注意:您可以重写正则表达式,以便过滤掉空字符串变得不必要)。

如果你想要做的就是将文本分成中文字符,那么你现在已经完成了很多工作。不确定OP的“单词”概念是什么,但对我来说,这是一个句子可能同样分为这个是的一|个|句子以及这是|一个|句子,取决于你的观点。但是,任何超出(可能由此组成)字符和字符类(符号与空格与字母等)概念的内容都远远超出了内置于unicode和python中的内容。你需要一些自然语言处理才能做到这一点。让我说一下,虽然您的示例'yes the United Nations can!'.split()确实成功地证明了split方法对大量数据有用,但它并没有正确地将英文文本解析为单词:它无法将United Nations识别为一个单词,虽然它错误地认为can!是一个单词,但显然不是。这种方法同时给出误报和漏报。根据您的数据和您打算完成的任务,这可能是您想要的,也可能不是。

答案 2 :(得分:4)

像中文这样的语言对单词的定义非常流畅。例如。 ma的一个含义是“马”。 shang的一个含义是“在...之上”或“在...之上”。一种化合物是“马上”,意思是字面意思是“在马背上”,但用比喻意思是“立即”。你需要一个非常好的词典,里面有化合物,查找词典需要一个最长的匹配方法。复杂的东西充斥着德语(着名的例子就像“多瑙河蒸汽导航公司的导演的妻子”,用一个词来表达),突厥语,芬兰语和马扎尔语 - 这些语言有很长的单词,其中很多都不会在一本字典,需要分解才能理解它们。

你的问题是语言学之一,与Python无关。

答案 3 :(得分:2)

好的我明白了。

只需使用list():

即可完成所需
>>> list(u"这是一个句子")
[u'\u8fd9', u'\u662f', u'\u4e00', u'\u4e2a', u'\u53e5', u'\u5b50']

感谢您的所有投入。

答案 4 :(得分:1)

日语部分可能,在这个词的开头和结尾通常有不同的字符类,但是有关于这个主题的全部科学论文供中文使用。如果您有兴趣,我有一个用日语分词的正则表达式:http://hg.hatta-wiki.org/hatta-dev/file/cd21122e2c63/hatta/search.py#l19

答案 5 :(得分:1)

答案 6 :(得分:1)

最适合中文的分词器工具是pynlpir。

import pynlpir
pynlpir.open()
mystring = "你汉语说的很好!"
tokenized_string = pynlpir.segment(mystring, pos_tagging=False)

>>> tokenized_string
['你', '汉语', '说', '的', '很', '好', '!']

请注意,pynlpir在许可方面存在着一个臭名昭著但易于解决的问题,您可以在互联网上找到许多解决方案。 您只需要替换NLPIR文件夹中的NLPIR.user文件,即可从此repository下载有效许可证并重新启动环境。

答案 7 :(得分:0)

list() 是中文唯一句子的答案。在大多数情况下,对于那些混合英语/中文。它在hybrid-split处回答,只需从Winter复制答案,如下所示。

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

答案 8 :(得分:0)

  

如果str的长度超过30,则采用27个字符,并在末尾添加“ ...”
  否则返回str

str='中文2018-2020年一区6、8、10、12号楼_「工程建设文档102332号」'
result = len(list(str)) >= 30 and ''.join(list(str)[:27]) + '...' or str