Php 把一个句子分成几个单词
我需要把一个中文句子分成几个单独的单词。中文的问题是没有空格。例如,这个句子可能看起来像:Php 把一个句子分成几个单词,php,cjk,multibyte,text-segmentation,Php,Cjk,Multibyte,Text Segmentation,我需要把一个中文句子分成几个单独的单词。中文的问题是没有空格。例如,这个句子可能看起来像:主楼怎么走(带空格的是:主楼 怎么 走) 目前我能想出一个解决办法。我有一本中文字典(在数据库里)。脚本将: 尝试在数据库中查找句子的前两个字符(主楼) 如果主楼实际上是一个单词,它在数据库中,脚本将尝试查找前三个字符(主楼怎)主楼怎不是一个单词,因此它不在数据库中=>我的应用程序现在知道主楼是一个单独的单词 试着用剩下的角色来做 我真的不喜欢这种方法,因为即使分析一个小文本,它也会多次查询数据库 有其他解
主楼怎么走代码>(带空格的是:主楼 怎么 走代码>)
目前我能想出一个解决办法。我有一本中文字典(在数据库里)。脚本将:
尝试在数据库中查找句子的前两个字符(主楼代码>)
如果主楼
实际上是一个单词,它在数据库中,脚本将尝试查找前三个字符(主楼怎代码>)<代码>主楼怎
不是一个单词,因此它不在数据库中=>我的应用程序现在知道主楼
是一个单独的单词
试着用剩下的角色来做
我真的不喜欢这种方法,因为即使分析一个小文本,它也会多次查询数据库
有其他解决方案吗?好吧,如果你有一个包含所有单词的数据库,并且没有其他方法可以让这些单词参与进来,我想你不得不重新查询数据库 您可以构建非常长的正则表达式
编辑:
我想用数据库中的脚本自动构建它。不写
手。 你可能想考虑使用一个数据结构。首先从字典中构造trie,然后搜索有效单词会快得多。这样做的好处是很快就可以确定你是在一个单词的末尾,还是需要继续寻找更长的单词。你可以输入文本、句子、段落等等。因此,是的,您对它的处理将需要针对每次检查对您的DB进行查询
不过,如果对word列进行适当的索引,您就不会有太多问题
话虽如此,这本词典有多大?毕竟,你只需要这些词,而不是它们的定义来检查它是否是一个有效的词。因此,如果可能的话(取决于大小),拥有一个只包含键(实际单词)的巨大内存映射/哈希表/字典可能是一种选择,而且速度会非常快
在1500万字的情况下,假设平均7个字符@2个字节,每个字符都在200兆字节左右。不太疯狂
编辑:在“仅”100万字的情况下,您将看到大约超过13兆字节,比如说15兆字节的开销。我想说,这是一个很简单的问题。为了提高性能,在将句子插入数据库并自己添加空格之前,您不能进行所有这些检查吗?(为了简单起见,使用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是不可能的,因此该分支无效,这意味着第一个必须是真正的解释
我认为这种方法可以最大限度地减少对数据库的调用次数(尽管它可能会从数据库返回更大的集合,因为您正在获取所有以相同字符开头的单词集合)。如果你的数据库为这种搜索建立了索引,我认为这比逐字逐句地搜索效果更好。现在看看整个过程和其他答案,我认为这实际上是一个trie结构(假设搜索的角色是树的根),正如另一张海报所建议的那样。好吧,这是这个想法的一个实现 这是计算语言学中相当标准的任务。它的名字叫“标记化”或“分词”。试着搜索“中文分词”或“中文标记化”,你会发现一些已经用来完成这项任务的工具,以及关于做这项工作的研究系统的论文
要做到这一点,您通常需要使用通过在相当大的训练语料库上运行机器学习系统构建的统计模型。您可以在网上找到的一些系统都带有经过预培训的模型。感谢大家的帮助
经过一点研究,我发现了一些有用的工具(记住了你所有的建议),这就是为什么我要回答我自己的问题
一个PHP类()
Drupal模块,基本上是另一个PHP解决方案,具有4种不同的分段算法(很容易理解它是如何工作的)()
用于中文分词的PHP扩展()
如果您尝试在百度搜索“搜索”,还有其他一些解决方案可用中文分词"
真诚地
eq我确实意识到中文分词问题是一个非常复杂的问题,但在某些情况下,这种简单的算法可能就足够了:搜索从第I个字符开始的最长单词w,然后重新开始搜索I+长度(w)第I个字符
下面是一个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))
一种快速有效的中文文本分割方法是基于最大匹配分段的方法
33. 你 | 可以 | 叫 | 我 | 夏 | 尔
戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末。星期日 | 早晨,伊 | 冯 | 娜 | 无意中 | 走进 | 浴室,正巧 | 将军 | 在 | 洗 | 盆浴。她 | 感到 | 非常 | 意外,不禁 | 大 | 叫 | 一声:“我的 | 上帝!”
戴高乐 | 于是 | 转 | 过 | 身,看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口。他 | 继续 | 用 | 香皂 | 擦 | 身,不 | 紧 | 不 | 慢 | 地 | 说:“伊 | 冯 | 娜,你 | 知道,如果 | 是 | 我们 | 之间 | 的 | 隐私,你 | 可以 | 叫 | 我 | 夏 | 尔,用不着 | 叫 | 我 | 上帝……”
你 | 可以 | 叫 | 我 | 夏 | 尔 | 戴高乐 | 将军 | 和 | 夫人 | 在 | 科隆 | 贝 | 双 | 教堂 | 村 | 过 | 周末 | 星期日 | 早晨 | 伊 | 冯 | 娜 | 无意中 | 走进 | 浴室 | 正巧 | 将军 | 在 | 洗 | 盆浴 | 她 | 感到 | 非常 | 意外 | 不禁 | 大 | 叫 | 一声 | 我的 | 上帝 | 戴高乐 | 于是 | 转 | 过 | 身 | 看见 | 妻子 | 因 | 惊魂 | 未定 | 而 | 站立 | 在 | 门口 | 他 | 继续 | 用 | 香皂 | 擦 | 身 | 不 | 紧 | 不 | 慢 | 地 | 说 | 伊 | 冯 | 娜 | 你 | 知道 | 如果 | 是 | 我们 | 之间 | 的 | 隐私 | 你 | 可以 | 叫 | 我 | 夏 | 尔 | 用不着 | 叫 | 我 | 上帝