将一个句子分成单独的单词

问题描述 投票:13回答:11

我需要将一个中文句子分成单独的单词。中文的问题是没有空格。例如,句子可能看起来像:主楼怎么走(带空格的是:主楼 怎么 走)。

目前我可以想到一个解决方案。我有一个带有中文单词的字典(在数据库中)。该脚本将:

  1. 尝试在数据库中找到句子的前两个字符(主楼),
  2. 如果主楼实际上是一个单词并且它在数据库中,脚本将尝试找到前三个字符(主楼怎)。 主楼怎不是一个单词,所以它不在数据库中=>我的应用程序现在知道主楼是一个单独的单词。
  3. 尝试与其余的角色一起做。

我真的不喜欢这种方法,因为即使是一个小文本,它也会查询数据库太多次。

还有其他解决方案吗?

php cjk multibyte text-segmentation
11个回答
6
投票

感谢大家的帮助!

经过一番研究后,我找到了一些工具(考虑到你的所有建议),这就是我回答自己问题的原因。

  1. 一个PHP类(http://www.phpclasses.org/browse/package/2431.html
  2. 一个Drupal模块,基本上是另一个PHP解决方案,有4种不同的分割算法(很容易理解它是如何工作的)(http://drupal.org/project/csplitter
  3. 中文分词的PHP扩展(http://code.google.com/p/phpcws/
  4. 如果您尝试在baidu.com上搜索“中文分词”,还可以使用其他一些解决方案

此致

E区


-1
投票

这是计算语言学中相当标准的任务。它的名称是“标记化”或“分词”。尝试搜索“中文分词”或“中文标记化”,您将找到为完成此任务而制作的几种工具,以及有关研究系统的文章。

为了做到这一点,您通常需要使用通过在相当大的训练语料库上运行机器学习系统而构建的统计模型。您可以在网上找到的几个系统都配有预先训练的模型。


-3
投票

您可以构建非常长的正则表达式。

编辑:我的意思是使用DB中的脚本自动构建它。不要手工编写。


2
投票

您可能需要考虑使用trie数据结构。首先从字典构造trie,然后搜索有效的单词会快得多。优点是确定你是否在一个单词的最后或需要继续寻找更长的单词是非常快。


1
投票

你有输入文本,句子,段落。所以,是的,您对它的处理将需要针对每个检查查询您的数据库。

但是,如果对单词列进行合适的索引,则不应该有太多问题。

话虽如此,这本字典有多大?毕竟,你只需要单词而不是它们的定义来检查它是否是一个有效的单词。因此,如果可能的话(取决于大小),拥有一个巨大的内存映射/哈希表/字典只有键(实际的单词)可能是一个选项,并将快速闪电。

在1500万字,比如平均每个2字节的7个字符,每个字符大约200兆字节。不要太疯狂

编辑:在'只有'100万字的情况下,你只需要超过13兆字节就可以看到,比如说只需要一些开销。我会说,这是一个明智的选择。


1
投票

另一个运作良好的是http://www.itgrass.com/phpanalysis/index.html

它是我发现的唯一一个与utf-8配合使用的产品。其余的只在gb18030中为我工作,后来引起了大量的问题。我以为我将不得不重新开始,但这个节省了我很多时间。


0
投票

好吧,如果你有一个包含所有单词的数据库,并且没有其他方法可以获得这些单词,我认为你被迫重新查询数据库。


0
投票

为了提高性能,你不能在将句子​​插入数据库之前进行所有这些检查,并自己添加空格吗?


0
投票

(为简单起见,使用ABCDE代表汉字)

假设您有'句子'ABCDE输入,并且您的词典包含以A:AB,ABC,AC,AE和ABB开头的这些词。并假设CDE这个词存在,但DE,E都没有。

解析输入句子时,从左到右,脚本会拉出第一个字符A.而不是查询数据库以查看A是否为单词,查询数据库以提取所有以A开头的单词。

循环遍历这些结果,从输入字符串中抓取下几个字符以获得正确的比较:

AB  ?= AB : True
ABC ?= ABC: True
AC  ?= AB : False
AE  ?= AB : False
ABB ?= ABC: False

在这一点上,该程序分解了它找到的两个“真正的”分支。首先,它假设AB是第一个单词,并试图找到C起始单词。找到CDE,因此可以进行分支。在另一个分支中,ABC是第一个单词,但DE不可能,因此分支无效,这意味着第一个必须是真正的解释。

我认为这种方法最大限度地减少了对数据库的调用次数(尽管它可能会从数据库返回更大的集合,因为你要获取所有以相同字符开头的单词集)。如果你的数据库被编入索引进行这种搜索,我认为这比逐字母更好。现在看看整个过程,以及其他答案,我认为这实际上是一个特里结构(假设搜索到的字符是树的根),正如另一张海报所暗示的那样。好吧,这是一个实现这个想法!


0
投票

我确实意识到中文分词问题是一个非常复杂的问题,但在某些情况下,这个简单的算法可能就足够了:搜索以第i个字符开头的最长单词w,然后再次为i + length(w)字符搜索。

这是一个Python实现:

#!/usr/bin/env python
# encoding: utf-8

import re
import unicodedata
import codecs

class ChineseDict:

    def __init__(self,lines,rex):
        self.words = set(rex.match(line).group(1) for line in lines if not line.startswith("#"))
        self.maxWordLength = max(map(len,self.words))

    def segmentation(self,text):
        result = []
        previousIsSticky = False
        i = 0
        while i < len(text):
            for j in range(i+self.maxWordLength,i,-1):
                s = text[i:j]
                if s in self.words:
                    break
            sticky = len(s)==1 and unicodedata.category(s)!="Lo"
            if previousIsSticky or (result and sticky):
                result[-1] += s
            else:
                result.append(s)
            previousIsSticky = sticky
            i = j
        return u" | ".join(result)

    def genWords(self,text):
        i = 0
        while i < len(text):
            for j in range(i+self.maxWordLength,i,-1):
                s = text[i:j]
                if s in self.words:
                    yield s
                    break
            i = j


if __name__=="__main__":
    cedict = ChineseDict(codecs.open("cedict_ts.u8",'r','utf-8'),re.compile(r"(?u)^.+? (.+?) .+"))
    text = u"""33. 你可以叫我夏尔
    戴高乐将军和夫人在科隆贝双教堂村过周末。星期日早晨,伊冯娜无意中走进浴室,正巧将军在洗盆浴。她感到非常意外,不禁大叫一声:“我的上帝!”
    戴高乐于是转过身,看见妻子因惊魂未定而站立在门口。他继续用香皂擦身,不紧不慢地说:“伊冯娜,你知道,如果是我们之间的隐私,你可以叫我夏尔,用不着叫我上帝……”
    """
    print cedict.segmentation(text)
    print u" | ".join(cedict.genWords(text))

最后一部分使用CCEDICT dictionary的副本来分割两种风格的(简化的)中文文本(分别有和没有非单词字符):

33. 你 | 可以 | 叫 | 我 | 夏 | 尔
    戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末。星期日 | 早晨,伊 | 冯 | 娜 | 无意中 | 走进 | 浴室,正巧 | 将军 | 在 | 洗 | 盆浴。她 | 感到 | 非常 | 意外,不禁 | 大 | 叫 | 一声:“我的 | 上帝!”
    戴高乐 | 于是 | 转 | 过 | 身,看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口。他 | 继续 | 用 | 香皂 | 擦 | 身,不 | 紧 | 不 | 慢 | 地 | 说:“伊 | 冯 | 娜,你 | 知道,如果 | 是 | 我们 | 之间 | 的 | 隐私,你 | 可以 | 叫 | 我 | 夏 | 尔,用不着 | 叫 | 我 | 上帝……”

你 | 可以 | 叫 | 我 | 夏 | 尔 | 戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末 | 星期日 | 早晨 | 伊 | 冯 | 娜 | 无意中 | 走进 | 浴室 | 正巧 | 将军 | 在 | 洗 | 盆浴 | 她 | 感到 | 非常 | 意外 | 不禁 | 大 | 叫 | 一声 | 我的 | 上帝 | 戴高乐 | 于是 | 转 | 过 | 身 | 看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口 | 他 | 继续 | 用 | 香皂 | 擦 | 身 | 不 | 紧 | 不 | 慢 | 地 | 说 | 伊 | 冯 | 娜 | 你 | 知道 | 如果 | 是 | 我们 | 之间 | 的 | 隐私 | 你 | 可以 | 叫 | 我 | 夏 | 尔 | 用不着 | 叫 | 我 | 上帝 

0
投票

分割中文文本的一种好的快速方法是基于最大匹配分割,它基本上将测试不同长度的单词以查看最有可能的分割组合。它会列出所有可能的单词。

在这里阅读更多相关信息:http://technology.chtsai.org/mmseg/

这就是我在我的读者(DuZhe)文本分析器(http://duzhe.aaginskiy.com)中使用的方法。我不使用数据库,实际上我将一个单词列表预先加载到一个数组中,该数组占用大约2MB的RAM,但执行速度非常快。

如果你正在考虑使用词法分割而不是统计学(虽然根据一些研究统计方法可以达到~97%),一个非常好的分割工具是ADSOtrans,可以在这里找到:http://www.adsotrans.com

它使用数据库,但有很多冗余表来加速分段。您还可以提供语法定义以帮助进行细分。

© www.soinside.com 2019 - 2024. All rights reserved.