Board logo

标题: [文本处理] 批处理中文分词的实现 [打印本页]

作者: cjiabing    时间: 2010-9-14 18:39     标题: 批处理中文分词的实现

虽然没多少人关注这个话题,但还是发个帖,开个头,希望有后来人。
       ——以下两段代码只能说是“实现了功能”,但效率很差。假如是自制的小字典还可以,使用大词库就非常吃力了。经测试,使用大词库,一句话可能要一两分钟才能切分词语!~
       ——分词好像是好多学校计算机专业的作业题哦,搜索过几篇分词的学生毕业论文,有些IT大企业面试题……
       ——这个帖子重点讨论使用批处理进行中文分词的可能性,欢迎你参与……
       以下是相关帖:
      
http://www.bathome.net/thread-9183-1-1.html
       词库下载:
http://cjiabing.qupan.com/6627766.html
       中文分词介绍:
http://baike.baidu.com/view/19109.htm
       文章介绍:
http://hi.baidu.com/cjiabing/blog/item/4c95b91bf3abaa178718bff4.html
        中文分词规范:
http://wenku.baidu.com/view/1dbd3fc58bd63186bcebbc7d.html

分词介绍

  1. 分词,简单地说,就是将一句话拆解/切分成独立的词语。
  2. 通常,句子由字和词组成,分词就是将句子变回字和词。
  3. 如句子:
  4. 我喜欢音乐。
  5. 分词就是把句子中的词语用空格等方式分开,变成:
  6. 我 喜欢 音乐。
  7. 分词的目的是为了提取句子中的词语/关键词以便做搜索、摘要等等用途。
  8. 分词属于计算机自然语言处理的基础,是有难度的,如上例的切分结果可能有两种:
  9. 1、我 | 喜欢 | 音乐。
  10. 2、我 | 喜 | 欢音 | 乐。
  11. 要正确分词需要“消除歧义”。另外,还要“登陆新词”,也就是如新词、姓名等如何切分?
复制代码


测试题

  1. 以下是测试题,用以测试你分词的准确率:
  2. 我喜欢坐在面包车上一边吃面包一边看北京天安门门楼的门
  3. 我喜欢坐在面包车上一边吃面包一边看北京大学生活动中心门楼的门
  4. 我喜欢坐在面包车上一边吃面包一边看北京大学生活在音乐里
  5. 我喜欢坐在面包车上一边吃面包一边看北京大学生活很好
  6. 我喜欢坐在面包车上一边吃面包一边看北京大学的学生生活在音乐里
  7. 我喜欢坐在面包车上一边吃面包一边看北京大学的学生活在音乐里
  8. 广西大学生活很好
  9. 我喜欢吃鸭肉
  10. 我喜欢音乐
  11. 我喜欢吃面条
  12. 伟大学生生活
  13. 大学生活动
  14. 大学生活动中心
  15. 大学生活好
  16. 发展中国家庭养猪事业
  17. 你说的确实在理
  18. 中华人民共和国万岁
  19. 吃面包
复制代码

批处理中文分词之正向最大匹配法

  1. @ECHO OFF&SETLOCAL ENABLEDELAYEDEXPANSION
  2. :FJJZ
  3. CLS
  4. SET /P INF=    你对电脑说:
  5. ECHO.
  6. CD.>S.TXT
  7. FOR /L %%I IN (0,1,100) DO (
  8. SET STRDD=!INF:~%%I,4!&IF DEFINED STRDD SET STRDDO=!STRDD:~3,1!&IF DEFINED STRDDO   FOR /F "TOKENS=1,*"  %%A IN (DICTIONARY.TXT) DO IF "%%A"=="!STRDD!" ECHO 4 %%I %%A %%B      %%I,4
  9. REM 截取四个字组成的词语,如“刻舟求剑”————意味着缺陷:四个字以上的词语如“中华人民共和国”不在截取范围。
  10. SET STRCC=!INF:~%%I,3!&IF DEFINED STRCC SET STRCCO=!STRDD:~2,1!&IF DEFINED STRCCO   FOR /F "TOKENS=1,*"  %%E IN (DICTIONARY.TXT) DO IF "%%E"=="!STRCC!" ECHO 3 %%I %%E %%F      %%I,3
  11. REM 截取三个字组成的词语,如“批处理”————前面判断字符是否赋值,后面用于与词库匹配。
  12. SET STRBB=!INF:~%%I,2!&IF DEFINED STRBB SET STRBBM=!STRBB:~1,1!&IF DEFINED STRBBM   FOR /F "TOKENS=1,*"  %%X IN (DICTIONARY.TXT) DO IF "%%X"=="!STRBB!" ECHO 2 %%I %%X %%Y      %%I,2
  13. REM 截取两个字组成的词语,如“胜利”————后面结果保留字符长度和偏移量。
  14. SET STRAA=!INF:~%%I,1!&IF DEFINED STRAA FOR /F "TOKENS=1,*"  %%M IN (DICTIONARY.TXT) DO IF "%%M"=="!STRAA!" ECHO 1 %%I %%M %%N       %%I,1
  15. REM 截取一个字组成的词语,如“锅”。
  16. )>>S.TXT
  17. REM 以上代码很好地解释了字符串的截取和匹配,思路很清晰,但效率很差,原因在于每截取一个字词就匹配一下词库,FOR使用得太频繁。
  18. rem 一般我写代码都是,首先着重在于描述过程,达到目的后优化代码,提高效率。我是按人的思路写代码,不是按机器的思路写代码,所以效率差了。
  19. REM 改良的思路应该是,先将截取结果写入一个临时文件,然后使用“FINDSTR /I /X /G:TMP.TXT   DICTIONARY.TXT>>S3.TXT”与词库匹配,试了一下,效果明显提高。有空再改。
  20. SET VAR=-1
  21. ECHO 切分结果:
  22. ECHO.
  23. :LP
  24. SET /A VAR+=1
  25. FOR /F "TOKENS=1,2,3,*"   %%I IN (S.TXT) DO (
  26. IF !VAR!==%%J (
  27. SET /P K= 【%%K】<NUL
  28. REM ECHO %%K 1
  29. SET /A VAR=%%J+%%I
  30. )
  31. )
  32. IF %VAR% LSS 30 GOTO LP
  33. REM 上面这个比较简单,就是将切分出来的词语按照字符长度和偏移量重新组装句子,并用空格和【】隔开以突出词语。
  34. REM 前面有过一段时间为这几行字母折腾了好几天,后来想通了原来竟是这么简单。
  35. ECHO.
  36. ECHO.
  37. PAUSE
  38. GOTO FJJZ
复制代码

批处理中文分词之交集型词语匹配

(包含正向最大匹配)

  1. @echo off&setlocal enabledelayedexpansion
  2. :fjjz
  3. cls
  4. set /p inf=    输入句子:
  5. cd.>S.TXT
  6. for /l %%i in (0,1,100) do (
  7. set strdd=!inf:~%%i,4!&if defined strdd set strddo=!strdd:~3,1!&if defined strddo   for /f "tokens=1,*" %%a in
  8. (dictionary.txt) do if "%%a"=="!strdd!" echo 4 %%i %%a %%b      %%i,4
  9. set strcc=!inf:~%%i,3!&if defined strcc set strcco=!strdd:~2,1!&if defined strcco   for /f "tokens=1,*" %%e in
  10. (dictionary.txt) do if "%%e"=="!strcc!" echo 3 %%i %%e %%f      %%i,3
  11. set strbb=!inf:~%%i,2!&if defined strbb set strbbm=!strbb:~1,1!&if defined strbbm   for /f "tokens=1,*" %%x in
  12. (dictionary.txt) do if "%%x"=="!strbb!" echo 2 %%i %%x %%y      %%i,2
  13. set straa=!inf:~%%i,1!&if defined straa for /f "tokens=1,*" %%m in (dictionary.txt) do if "%%m"=="!straa!" echo 1 %%i %%m %
  14. %n       %%i,1
  15. )>>S.TXT
  16. cd.>zk1.txt
  17. set var=-1
  18. :lp
  19. set /a var+=1
  20. for /f "tokens=1,2,3,*"   %%i in (S.txt) do (
  21. if !var!==%%j (
  22. set /p k= %%k<nul
  23. set /a var=%%j+%%i
  24. )
  25. )>>zk1.txt
  26. if %var% lss 30 goto lp
  27. echo.
  28. cd.>zk2.txt
  29. for /f "tokens=1,2,3,*"   %%a in (S.txt) do if %%a==2 set /p s= %%c<nul>>zk2.txt
  30. set /p wj=<zk1.txt
  31. set /p wj1=<zk2.txt
  32. for    %%i in (!wj!) do (
  33. for    %%a in (!wj!) do (
  34. set dfa=%%i
  35. set ahe=!dfa:~2,1!
  36. for    %%e in (!wj1!) do (
  37. for    %%x in (!wj1!) do (
  38. if defined ahe if "%%i%%a"=="%%e%%x" set vdae=%%e&set vdaea=%%x&set vdbe=%%i&set vdbea=%%a
  39. )
  40. )
  41. )
  42. )
  43. echo.
  44. set /p lied=<zk1.txt
  45. for   %%i in (!lied!) do (
  46. if "%%i"=="!vdbe!" (echo !vdae! !vdaea!) else (if not "!vdaea:~1,1!"=="%%i" echo %%i)
  47. )
  48. echo.
  49. pause
  50. del zk1.txt,zk2.txt
  51. goto fjjz
复制代码


作者: Batcher    时间: 2010-9-14 19:05

大文本处理,Perl脚本可能比较高效。
作者: cjiabing    时间: 2010-9-14 19:15

原帖由 Batcher 于 2010-9-14 19:05 发表
大文本处理,Perl脚本可能比较高效。

谢谢!~
我想学点其他处理大文本的技术,大大给点建议,如何?
作者: 523066680    时间: 2010-9-14 19:54

=.= 那句话就是建议……
作者: Batcher    时间: 2010-9-14 20:49     标题: 回复 3楼 的帖子

可以学习一下sed和gawk,大多时候比BAT高效。
Perl脚本一般来说更加高效,但缺点是需要安装一个环境才能执行,不像sed和gawk直接复制一下exe文件就行。
关键还是看自己对哪个方面更加感兴趣吧。
作者: cjiabing    时间: 2010-9-14 21:05

原帖由 Batcher 于 2010-9-14 20:49 发表
可以学习一下sed和gawk,大多时候比BAT高效。
Perl脚本一般来说更加高效,但缺点是需要安装一个环境才能执行,不像sed和gawk直接复制一下exe文件就行。
关键还是看自己对哪个方面更加感兴趣吧。

很好!谢谢!~
当时也想过用SED,但想想还是用纯P。因为SED没用过,需要重头去学。而当时我比较有想法了就先写P了,也是想从另外的角度试试看批处理是否能做这件事情,虽然我相信批处理能做,但结果效率太差了。
————————————————————————————————
感觉我的分词准确率比一些由C、java等的写的分词小程序要高——一次性通过一些公开的测试题目检测。但速度就不敢恭维了,我的一句话就用时一两分钟,人家的1万字才1秒,10000字以内的文章完全切分,用时间不超过1秒!——看来真的要改行才行,否则辜负了聪明!~
作者: 随风    时间: 2010-9-15 01:17

for /l %%i in (0,1,100) do (
set strdd=!inf:~%%i,4!&if defined strdd set strddo=!strdd:~3,1!&if defined strddo   for /f "tokens=1,*" %%a in (dictionary.txt) do if "%%a"=="!strdd!" echo 4 %%i %%a %%b      %%i,4
set strcc=!inf:~%%i,3!&if defined strcc set strcco=!strdd:~2,1!&if defined strcco   for /f "tokens=1,*" %%e in (dictionary.txt) do if "%%e"=="!strcc!" echo 3 %%i %%e %%f      %%i,3
set strbb=!inf:~%%i,2!&if defined strbb set strbbm=!strbb:~1,1!&if defined strbbm   for /f "tokens=1,*" %%x in (dictionary.txt) do if "%%x"=="!strbb!" echo 2 %%i %%x %%y      %%i,2
set straa=!inf:~%%i,1!&if defined straa for /f "tokens=1,*" %%m in (dictionary.txt) do if "%%m"=="!straa!" echo 1 %%i %%m %%n       %%i,1
)>>S.TXT

批处理作这个也许是真的存在效率问题,但也应该不至于这么慢吧,本想研究下你的代码,但看到了上面这一堆,就有种想吐的感觉,起码的分行和缩进都不会吗?你的代码不打算让别人看的吗?不知道你的 dictionary.txt 文件有多大,这样循环嵌套速度快才是怪事。有兴趣的话最好把意图用文字描述清楚,看看别人是否能写出比你效率高的代码来。。
作者: netbenton    时间: 2010-9-15 01:40

批处理的效率虽然是比不上其它的XX,但是,也因为批处理的特点,往往是可以取巧的!!

下面的代码,实现词拆分查表功能,可以在一秒内完成:
注意:dictionary.txt 词库,需以较长的词在前,短在后的顺序排列
  1. @echo off&setlocal enabledelayedexpansion
  2. :lp
  3. set str=
  4. echo;
  5. echo;直接回车退出测试
  6. set /p str=输入一个字符串:
  7. if "!str!" equ "" goto :eof
  8. setlocal enabledelayedexpansion
  9. set/a z=8180,x=0&for /l %%a in (1,1,14) do (set/a "y=(z-x)/2+x"
  10.     for %%b in (!y!) do if "!str:~%%b,1!" equ "" (set/a z=y) else (set/a x=y)
  11. )
  12. ::计算输入串的长度
  13. ::sap 存入同等长度的空格串
  14. set sap=
  15. (for /l %%i in (0,1,!z!) do (
  16.     set "spa= !spa!"
  17.     set/a x=z-%%i
  18.     for /l %%j in (1,1,!x!) do (
  19.         echo;!str:~%%i,%%j!
  20.         set/a y=%%i+%%j
  21.         for %%a in ("!str:~%%i,%%j!") do (
  22.             set ##%%~a=%%i-!y!-%%j !##%%~a!
  23.         )
  24.     )
  25. ))>fen.tmp
  26. ::进行排列拆分到临时文件fen.tmp,同时取各分词的长度,及在原句中的起址位置,并按一定的规则保存
  27. set var=!str!
  28. for /f "tokens=*" %%a in ('findstr /x /i /g:fen.tmp dictionary.txt') do (
  29.   for %%k in (!##%%a!) do (
  30.     if "!var:%%a=!" neq "!var!" (
  31.       for /f "tokens=1,2,3 delims=-" %%b in ("%%k") do (
  32.         if "!var:~%%b,1!" neq " " (
  33.           echo;截取过程:[!var!]  %%a
  34.           set var=!var:~0,%%b!!spa:~,%%d!!var:~%%c!
  35.           set $$%%b=%%a
  36.         )
  37.       )
  38.     )
  39.   )
  40. )
  41. ::搜索词库,并做已经截取过记号,同时为恢复分词在原句中的位置做准备
  42. echo;
  43. if "!var: =!" neq "" (
  44.   echo;词库中没有的:
  45.   for %%a in (!var!) do (
  46.     for %%k in (!##%%a!) do (
  47.       if "!var:%%a=!" neq "!var!" (
  48.         for /f "tokens=1,2,3 delims=-" %%b in ("%%k") do (
  49.           if "!var:~%%b,1!" neq " " (
  50.             echo;截取过程:[!var!]  %%a
  51.             set var=!var:~0,%%b!!spa:~,%%d!!var:~%%c!
  52.             set $$%%b=%%a
  53.           )
  54.         )
  55.       )
  56.     )
  57.   )
  58. ) else (echo;所有字符均可在词库中到)
  59. ::对在词库中找不到的分词,也进行同样的处理
  60. echo;
  61. set dest=
  62. for /l %%a in (0,1,!z!) do set dest=!dest! !$$%%a!
  63. echo;最终拆分:!dest!
  64. endlocal&goto :lp
复制代码

[ 本帖最后由 netbenton 于 2010-9-15 03:32 编辑 ]
作者: cjiabing    时间: 2010-9-15 02:28

原帖由 随风 于 2010-9-15 01:17 发表

批处理作这个也许是真的存在效率问题,但也应该不至于这么慢吧,本想研究下你的代码,但看到了上面这一堆,就有种想吐的感觉,起码的分行和缩进都不会吗?你的代码不打算让别人看的吗?不知道你的 dictionary.txt  ...

       随风大哥,我解析一下你引用的代码,这四行用于截取输入的句子中的词语,第一个是截取四字词,如“肆无忌惮”,第二个是三字句,如“天安门”,第三个是二字词,如“北京”,第三个是一字词,如“门”。截取之后进行词语的匹配,就是将截取出来的词语查询词库,如果查询到就表示该成语成立,是一个多字词。在这里使用的是FOR,我知道FOR的效率不高,改用FINDSTR,但没改好。所以效率打折扣了,因时间关系,我希望大家帮忙优化,所以,你看到想吐是可以理解的。
       至于缩进,我知道看起来很美观,可是我很不适应,你如果知道每次我看到缩进的代码我都会取消缩进你就知道我多么讨厌缩进了,因为缩进仅仅是为了美观,有层次,但实际上对于一个懂得代码的人来说那毫无意义。而为了达到缩进而不停的敲击空格键更是浪费时间。我不反对缩进,但我本身是不习惯缩进的,请见谅。
       另外,我自己制作的词典很小,所以查询速度比较快,我下载的是去除拼音字母的标准词库213663词条.TXT,有1.60M,所以用四个FOR解析就有效率问题了。我知道有问题,因技术和时间的问题,现在无法优化,所以请大家帮忙……谢谢!~

[ 本帖最后由 cjiabing 于 2010-9-15 02:32 编辑 ]
作者: cjiabing    时间: 2010-9-15 02:37

原帖由 netbenton 于 2010-9-15 01:40 发表
批处理的效率虽然是比不上其它的XX,但是,也因为批处理的特点,往往是可以取巧的!!

下面的代码,实现词拆分查表功能,可以在一秒内完成:
注意:dictionary.txt 词库,需以较长的词在前,短在后的顺序排列@ec ...

谢谢netbenton兄的代码,有空慢慢研究,尝试了一下,似乎效果特别快,但不知道是否按既定要求执行,等我写完两篇论文在做答复,谢谢!~
______________________
netbenton兄,你把切分出来的词语做下处理,用空格隔开或者用其它标记标记一下,否则不知道你切分出来的词语在哪里!
代码很深奥,有空再慢慢研究,呵呵

[ 本帖最后由 cjiabing 于 2010-9-15 11:16 编辑 ]
作者: Batcher    时间: 2010-9-15 09:21     标题: 回复 9楼 的帖子

可以尝试一下支持自动缩进的编辑器,比如:Notepad++
作者: cjiabing    时间: 2010-9-15 11:25

原帖由 Batcher 于 2010-9-15 09:21 发表
可以尝试一下支持自动缩进的编辑器,比如:Notepad++


       平时偶尔也用 notepad++ 啊,但没弄出8L的效果来。 notepad++ 看起来眼花缭乱的,怕不小心出错就不敢用,除非为了查找和替换。
       平常都是直接用系统的记事本,习惯了竖着看代码。虽然缩进很有诗歌的形式美,也希望那样做,可我不习惯,每次看人家缩进的代码,我非得取消缩进才好理解——矣,人贱没办法。
作者: 随风    时间: 2010-9-15 12:42

代码缩进绝非仅仅是为了好看,更主要的作用是便于理解代码。通过缩进,别人可以很容易看清你的代码结构,哪些块是干什么用的,哪些语句组成一个语句块。。。。。。。。
把语句盲目的用 &  符号连接起来,会使人很难看明白哪些语句可以拆开来看,哪些语句是一个块中的。。。。
作者: netbenton    时间: 2010-9-16 21:39

8楼的代码,因为findstr 的一些特性,存在一些bug,修改如下:

一、词库搜索代码:
    findstr /x /g:fen.tmp /f:dics.txt

二、把词库按字数分别存放,并且文件名上可见词的字数。如:
[email=dict@1.txt]dict@1.txt[/email]
1人
1们
...

[email=dict@2.txt]dict@2.txt[/email]
2人民
2后门
...

......

些种办法把词分出来后,可方便词库的维护。
以下代码可把楼主提供的词库转换成按字数分文档存放。
  1. @echo off&setlocal enabledelayedexpansion
  2. for /l %%a in (0,1,10) do del dict@%%a.txt /q
  3. echo;%time%
  4. for /f "tokens=*" %%a in (dic16.txt) do (
  5.    set str=%%a
  6.    if "!str:~9!" equ "" (
  7.       set str=0123456789%%a
  8.       set n=!str:~-10,1!
  9.       echo;!n!%%a>>dict@!n!.txt
  10.    ) else (
  11.       echo;!n!%%a>>dict@10.txt
  12.    )
  13. )
  14. (for /f %%a in (dictionar@1.txt) do (echo;1%%a))>dict@1.txt
  15. dir /o-n /b dict@*>dics.txt
  16. echo;%time%
  17. pause
复制代码
三、dics.txt  存放各分词库的文件名,以字数长的分词库在前,短在后。
    dics.txt内容:
[email=dict@10.txt]dict@10.txt[/email]
[email=dict@9.txt]dict@9.txt[/email]
[email=dict@8.txt]dict@8.txt[/email]
[email=dict@7.txt]dict@7.txt[/email]
[email=dict@6.txt]dict@6.txt[/email]
[email=dict@5.txt]dict@5.txt[/email]
[email=dict@4.txt]dict@4.txt[/email]
[email=dict@3.txt]dict@3.txt[/email]
[email=dict@2.txt]dict@2.txt[/email]
[email=dict@1.txt]dict@1.txt[/email]

四、分词代码修改后,不再有搜索漏网的bug。并且对速度影响不大,20个字数长度的句子,拆分用时在一秒内。
  1. @echo off&setlocal enabledelayedexpansion
  2. :lp
  3. set str=
  4. echo;
  5. echo;直接回车退出测试
  6. set /p str=输入一个字符串:
  7. set ti=%time%
  8. if "!str!" equ "" goto :eof
  9. setlocal enabledelayedexpansion
  10. set/a z=8180,x=0&for /l %%a in (1,1,14) do (set/a "y=(z-x)/2+x"
  11.     for %%b in (!y!) do if "!str:~%%b,1!" equ "" (set/a z=y) else (set/a x=y)
  12. )
  13. ::计算输入串的长度
  14. ::sap 存入同等长度的空格串
  15. set lens=1 2 3 4 5 6 7 8 9 10 11
  16. set/a z1=z-1
  17. set sap=
  18. (for /l %%i in (0,1,!z1!) do (
  19.     set "spa= !spa!"
  20.     set one=!str:~%%i,1! !one!
  21.     set/a x=z-%%i
  22.     for %%j in (!lens!) do (
  23.         set/a y=%%i+%%j
  24.         echo;%%j!str:~%%i,%%j!
  25.         for %%a in ("!str:~%%i,%%j!") do (
  26.             set ##%%~a=%%i-!y!-%%j !##%%~a!
  27.         )
  28.     )
  29. ))>fen.tmp
  30. ::(for /l %%a in (0,1,!z!) do for %%k in (!@%%a!) do (echo;%%k))>fen.tmp
  31. ::进行排列拆分到临时文件fen.tmp,同时取各分词的长度,及在原句中的起址位置,并按一定的规则保存
  32. ::似乎要从短到长,findstr才能完全正确查找
  33. set var=!str!
  34. echo;!vvv!
  35. for /f "tokens=2* delims=:123456789" %%1 in ('findstr /x /g:fen.tmp /f:dics.txt') do (
  36.   for %%k in (!##%%2!) do (
  37.     if "!var:%%2=!" neq "!var!" (
  38.       for /f "tokens=1,2,3 delims=-" %%b in ("%%k") do (
  39.         if "!var:~%%b,%%d!" equ "!str:~%%b,%%d!" (
  40.           echo;截取过程:[!var!]  %%2
  41.           set var=!var:~0,%%b!!spa:~,%%d!!var:~%%c!
  42.           set $$%%b= [%%2]
  43.         )
  44.       )
  45.     )
  46.   )
  47. )
  48. ::搜索词库,并做已经截取过记号,同时为恢复分词在原句中的位置做准备
  49. echo;
  50. if "!var: =!" neq "" (
  51.   echo;词库中没有的:
  52.   for %%a in (!var!) do (
  53.     for %%k in (!##%%a!) do (
  54.       if "!var:%%a=!" neq "!var!" (
  55.         for /f "tokens=1,2,3 delims=-" %%b in ("%%k") do (
  56.           if "!var:~%%b,1!" neq " " (
  57.             echo;截取过程:[!var!]  %%a
  58.             set var=!var:~0,%%b!!spa:~,%%d!!var:~%%c!
  59.             set $$%%b= %%a
  60.           )
  61.         )
  62.       )
  63.     )
  64.   )
  65. ) else (echo;所有字符均可在词库中找到)
  66. ::对在词库中找不到的分词,也进行同样的处理
  67. echo;
  68. set dest=
  69. for /l %%a in (0,1,!z!) do set dest=!dest!!$$%%a!
  70. echo;最终拆分:!dest!
  71. echo;%ti%
  72. echo;%time%
  73. endlocal&goto :lp
复制代码
五、词在库中的先后,决定了匹配的优先顺序,只要改变词在库中的顺序,即可改变折分的准确性。

我另外写了一个词库维护的代码,可以改变词在库中的顺序,增加词到词库中。

初版只支持一次修改两个词顺序。代码如下:
  1. @echo off&setlocal enabledelayedexpansion
  2. echo;  ** 调整词序/增加词组 **
  3. set /p str1=输入第一字符串:
  4. set /p str2=输入第二字符串:
  5. if "!str1!" equ "!str2!" goto :eof
  6. set str=0123456789!str1!
  7. set n1=!str:~-10,1!
  8. set str=0123456789!str2!
  9. set n2=!str:~-10,1!
  10. set str1=!n1!!str1!
  11. set str2=!n2!!str2!
  12. if "!n1!" equ "!n2!" (
  13.   findstr /x "!str1!" dict@!n1!.txt>nul
  14.   if !errorlevel! equ 1 (
  15.         set str1=%str2%
  16.         set str2=%str1%
  17.         set xchg=y
  18.      findstr /x "!str1!" dict@!n1!.txt>nul
  19.      if !errorlevel! equ 1 set xchg=n
  20.   )
  21.   if "!xchg!" neq "n" (
  22.     copy dict@!n1!.txt fen.tmp /y
  23.     (for /f %%a in (fen.tmp) do (
  24.         if "%%a" neq "!str2!" echo;%%a
  25.         if "%%a" equ "!str1!" (
  26.           echo;!str2!
  27.           if defined xchg (echo;!str1!)
  28.         )
  29.     ))>dict@!n1!.txt
  30.     echo;修改 [!str1!] 和 [!str2!] 在dict@!n1!.txt中的顺序
  31.   ) else (
  32.     echo;!str2!>>dict@!n1!.txt
  33.     echo;!str1!>>dict@!n1!.txt
  34.     echo;添加 [!str1!] 和 [!str2!] 到 dict@!n1!.txt
  35.   )
  36. ) else (
  37.   findstr /x "!str1!" dict@!n1!.txt>nul
  38.   if !errorlevel! equ 1 (
  39.     echo;!str1!>>dict@!n1!.txt
  40.     echo;添加 [!str1!] 到 dict@!n1!.txt
  41.   )
  42.   findstr /x "!str2!" dict@!n2!.txt>nul
  43.   if !errorlevel! equ 1 (
  44.      echo;!str2!>>dict@!n2!.txt
  45.      echo;添加 [!str2!] 到 dict@!n2!.txt
  46.   )
  47. )
  48. pause
复制代码
拆分句子示例:

输入一个字符串:这也是造成肇事车辆屡屡逃逸的一个主要原因

截取过程:[这也是造成肇事车辆屡屡逃逸的一个主要原因]  主要原因
截取过程:[这也是造成肇事车辆屡屡逃逸的一个           ]  屡屡
截取过程:[这也是造成肇事车辆  逃逸的一个           ]  车辆
截取过程:[这也是造成肇事    逃逸的一个           ]  逃逸
截取过程:[这也是造成肇事      的一个           ]  一个
截取过程:[这也是造成肇事      的             ]  也是
截取过程:[这  造成肇事      的             ]  造成
截取过程:[这    肇事      的             ]  肇事
截取过程:[这            的             ]  这
截取过程:[             的             ]  的
所有字符均可在词库中找到
最终拆分: [这] [也是] [造成] [肇事] [车辆] [屡屡] [逃逸] [的] [一个] [主要原因]
开始: 6:44:02.45
结束: 6:44:03.20

楼主出的测试例子结果如下(词库的一些词经过顺序调整):
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京] [天安门] [门楼] [的] [门]
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京] [大学生活动中心] [门楼] [的] [门]
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京大学] [生活] [在] [音乐] [里]
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京大学] [生活] [很好]
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京大学] [的] [学生] [生活] [在] [音乐] [里]
最终拆分: [我] [喜欢] [坐在] [面包车] [上] [一边] [吃] [面包] [一边] [看] [北京大学] [的] [学生] [活在] [音乐] [里]
最终拆分: [广西] [大学生活] [很好]
最终拆分: [我] [喜欢] [吃] [鸭肉]
最终拆分: [伟] [大学生] [生活]
最终拆分: [大学生活] [动]
最终拆分: [大学生活] [好]
最终拆分: [大学生活动中心]
最终拆分: [发展] [中国家庭养猪] [事业]
最终拆分: [你说] [的] [确实在] [理]

[ 本帖最后由 netbenton 于 2010-9-17 07:31 编辑 ]
作者: netbenton    时间: 2010-9-17 07:47

经过几次测试实验,我认为,只要对词库进行一定的“驯化”,即调整顺序、合理的增/减词汇,应该可以正确拆分所有的句子。

这种分文档存放词库方法较为灵活,可以随意调整词的优先顺序。
比如:
在最长分词库前增加一个人名库,这样就可以,以最优先权去匹配人的名字。
把dics.txt  的内容改为:
name.txt
[email]dict@10.txt[/email]
[email]dict@9.txt[/email]
[email]dict@8.txt[/email]
[email]dict@7.txt[/email]
[email]dict@6.txt[/email]
[email]dict@5.txt[/email]
[email]dict@4.txt[/email]
[email]dict@3.txt[/email]
[email]dict@2.txt[/email]
[email]dict@1.txt[/email]

然后在name.txt里面存放人名/地名等:
3刘德华
3张曼玉
4比尔盖茨
5东方不败
2香港
2桂林

这样,name.txt 中的词即得到最优先权的匹配
作者: cjiabing    时间: 2010-9-17 09:17

1、findstr 改为使用“findstr  /i  /x  /r   /g:test2.TXT    _dictionary.txt>>test3.TXT”。这里我也糊涂,首先它要用到取消大小写的“/I”,然后用到“ /R        将搜索字符串作为一般表达式使用。”不懂得是文本的原因还是Findstr本身的原因。
       2、你说的词库其实它是有分级的,按照我的理解,它至少有三级,第一级是字典,就是查单个字的,第二级是词典,查那些标准词语的,第三级是词频统计出来的出现频率比较多的词语,比如“你的”、“是吗”、“来得”等等。第四级是专业字典,比如人名地名字典。现在我们涉及的主要是第二级和第三级,因为没建立基础模式,我们还没用到字典来查单个词语。更关键的,制作词库非普通人能完成,所以,还得用现有的单一词库。
       3、据网上有帖子分析百度的分词方法提到,百度使用了正向最大匹配法和拼音匹配,速度都算快。百度还有最宝贵的一个字典就是人名、地名字典,用这个专业字典先匹配人名和地名,剩下的使用正向最大匹配法解决。
中科院软件所张俊林:Baidu分词算法分析_站长茶馆_站长在线_中国...
       4、分词的方法很多,现在我们尝试的都是最基本的方法,我发现你有意识的往“新词登录”方面想了,成绩也突出,很好!~工作虽然繁杂,但值得尝试批处理在这方面的能力,希望能为后面的自然处理打下基础。
       5、最后谢谢你,能和你一起交流是一件愉快的事情!~

[ 本帖最后由 cjiabing 于 2010-9-17 09:55 编辑 ]
作者: Batcher    时间: 2010-9-17 11:53

华为二十四口交换机

类似这种可以分吗?
作者: cjiabing    时间: 2010-9-17 12:31

原帖由 Batcher 于 2010-9-17 11:53 发表

类似这种可以分吗?

    你对电脑说:华为二十四口交换机
开始时间:12:30:31.07
输出截取时间:12:30:31.18
开始匹配时间:12:30:31.20
匹配结束时间:12:30:31.95
匹配与截取融合时间:12:30:32.03
开始组句时间:12:30:32.03
组句完成时间:12:30:33.57
获取二字词时间:12:30:33.59
交叉词开始时间:12:30:33.59
华为
二十四
口交
换机
最后完成时间:12:30:33.67
请按任意键继续. . .
作者: netbenton    时间: 2010-9-17 12:43

可以吧~~~,结果如下:

输入一个字符串:华为二十四口交换机

截取过程:[华为二十四口交换机]  二十四
截取过程:[华为   口交换机]  交换机
截取过程:[华为   口   ]  华为
截取过程:[     口   ]  口

所有字符均可在词库中找到

最终拆分: [华为] [二十四] [口] [交换机]
12:36:47.53
12:36:47.98


输入一个字符串:华为24口交换机
截取过程:[华为24口交换机]  交换机
截取过程:[华为24口   ]  华为
截取过程:[  24口   ]  口
词库中没有的:
截取过程:[  24    ]  24
最终拆分: [华为] 24 [口] [交换机]
12:45:33.93
12:45:34.34

[ 本帖最后由 netbenton 于 2010-9-17 12:46 编辑 ]
作者: cjiabing    时间: 2010-9-17 12:48

哈哈,我的变口交了
作者: cjiabing    时间: 2010-9-17 12:50

原帖由 netbenton 于 2010-9-17 12:43 发表
可以吧~~~,结果如下:

输入一个字符串:华为二十四口交换机

截取过程:[华为二十四口交换机]  二十四
截取过程:[华为   口交换机]  交换机
截取过程:[华为   口   ]  华为
截取过程:[     口   ]  口

...

你查查你词库里有没有口交这个词?
作者: netbenton    时间: 2010-9-17 12:56

输入一个字符串:华为二十四口交不好

截取过程:[华为二十四口交不好]  二十四
截取过程:[华为   口交不好]  不好
截取过程:[华为   口交  ]  华为
截取过程:[     口交  ]  口交

所有字符均可在词库中找到

最终拆分: [华为] [二十四] [口交] [不好]
12:55:39.01
12:55:39.48

库里有那个词的,只是没有匹配到它
作者: cjiabing    时间: 2010-9-17 13:11

原帖由 netbenton 于 2010-9-17 12:56 发表
输入一个字符串:华为二十四口交不好

截取过程:[华为二十四口交不好]  二十四
截取过程:[华为   口交不好]  不好
截取过程:[华为   口交  ]  华为
截取过程:[     口交  ]  口交

所有字符均可在词库中找 ...

哈哈,你也发明一个替换法啊,我见过有个家伙说取消或者增加一个字来切分的。
Batcher的这道题N多年以前见过,不过那时候还不懂分词。
“华为二十四口交换机”与“我喜欢坐在面包车上一边吃面包一边看北京大学生活动中心门楼的门”可能存在一定的冲突……
作者: batman    时间: 2010-9-19 09:26

虽然个人不太支持这个研究课题,但为楼主这种求真的精神而感动。。。
作者: TX_kakashi    时间: 2010-9-19 11:55     标题: 回复 24楼 的帖子

同意楼上的,lz钻研的精神值得敬佩,如果用高级语言的话也许能收获更多....毕竟这种工作不是批处理的强项....
作者: cjiabing    时间: 2010-9-19 15:46

原帖由 batman 于 2010-9-19 09:26 发表
虽然个人不太支持这个研究课题,但为楼主这种求真的精神而感动。。。


      还是netbenton厉害,有空向他学习!~
      这不过是每个人儿童时候的一个梦想而已,有朝一日机器人能与真正的走进我们的生活。学习批处理,让我们有了实现梦想的能力,像你说的,用批处理做这件事情就像用小刀宰牛一样——勉为其难了。
      电子科技的飞速发展促进了机器人的研究,现在的机器人在模仿人类的动作和功能方面已经有十分大的进步了。人工智能中最大的挑战来自语言智能,因为语言和思维的密切关系,语言智能了机器人也就智能了。但目前看来,思维这种活动似乎是人类特有的,机器人实现起来非常困难。我们连自己做的梦都没搞清楚,机器人又如何能搞清楚人的思维呢?
      人类文明之所以发展,一个原因就是人类对自然规律的不停探索,在语言智能方面亦如此。计算机说的话是计算机语言,我们说的话是自然语言,两种语言并不相同,计算机语言和自然语言如何翻译和转换正是当前研究的重点课题。这个课题又分为两大主题,一是计算机自然语言理解——计算机如何懂得我们人类说的话,一是自然语言的处理——理解之后的运用,如何让计算机自己思考并说出人类能够理解的话来,这叫做自然语言的生成。
      本帖谈的中文分词属于计算机自然语言理解的基础,它的作用是将一个句子拆解成一个个独立的、可以理解的词语。计算机的理解就是将句子还原成词语,并弄清楚词语之间内在的关系。用我们前面谈到的聊天机器人来说,你给它输入一句话“你吃饭了吗?”,你叫机器人如何回答呢?学过批处理的你可能会想,先设定一个文本,把各种问题和答案预先保存好,然后用输入的话作为搜索词,搜索这个文本,把结果显示出来就OK。目前,许多客服系统,专家系统用的就是这种方式。
       假如你输入“你吃饭了没有?”句子和上句有个别词出入,意思还是一样,那你的搜索词是什么了呢?假如你还是以句子来搜索,那你就得预先设置句子到文本中,但这样的结果就是这个文本将是无限的庞大——因为人说话的句子有时候句子词语长短都不一样,或许意思一样。
       可能你会想到用关键词来搜索,这才是搜索的正道,但关键词哪里来呢?来自输入的句子,那输入的句子你如何让电脑知道句子中哪个是词语,哪个不是?这时,你就需要分词了——将句子拆解开来,逐个查字典,字典有的就是词语,没有的就不是,词语多吧但总比句子少,所以,这个是比较可行的。
       英文的句子一般不用分词,原因是英文句子一般都要把单词用空格分开,但中文句子中的字和词都是粘到一起的,特别是古文,以前基本上没有标点符号的,区分词语完全靠人的语文经验判断。计算机要查找关键词首先要分词——用空格把句子中的词语隔开。这是中文在计算机自然语言处理方面遇到的一道坎,因为分词的准确率并非很高,速度也受到影响。
       现在中文分词在许多领域都有使用,比如搜索引擎、文献检索、专家系统、智能研究等等。你使用百度搜索的时候,假如你输入一个不是词语的词语,或者一段长长的话,百度搜索不到结果,往往会提示你“你搜索的是不是*……*”这就是百度的中文分词在起作用了。
       你可以使用论坛的搜索功能做试验,你试着同时搜索这三个关键词“我  爱   批处理”——之间用空格隔开,然后再搜索这句话“我爱批处理”。对比一下结果你就知道了,前面只能使用关键词来搜索,而后面使用了中文分词,自动把句子拆解成关键词。因为本论坛没有中文分词,所以搜索不到结果。
       专家系统和聊天机器人也是建立在这种中文分词的基础上的,它能自动识别你的句子,自动获得关键词,自动搜索答案,自动组句回答问题……
        由此可见,中文分词是未来人工智能发展的基础,它的重要性不言而喻。批处理中文分词的意义不过是做个尝试,如果不考虑效率问题也是可行的,只要你有足够的批处理知识和一定的语言学知识,假如再有好的词库和规则,用批处理实现你的梦想也是十分可能的事情……
        多了解、多尝试,说不定你会成为第一个智能机器人的发明家呢,而且还是中国制造的!~




欢迎光临 批处理之家 (http://www.bathome.net/) Powered by Discuz! 7.2