Board logo

标题: [文本处理] BAT批处理如何删除txt文本中相似/类似的内容? [打印本页]

作者: kabamei    时间: 2014-4-10 09:24     标题: BAT批处理如何删除txt文本中相似/类似的内容?

本帖最后由 pcl_test 于 2016-9-10 13:30 编辑

我在各个网站上面收集了很多语录,里面肯定有重复的,我想把重复的删除了。如果用论坛上面删除重复行的办法必须每一行完全一样才能删除。但是每个网站的排版又不尽相同,里面标点符号可能不同,发布的格式也有区别。

举例子:
A网站收集回来的为:
3.学然后知不足。—— 礼记
4.活到老学到老。—— 谚语
5.学习永远不晚。—— 高尔基
6.重复是学习之母。 —— 狄慈根
B网站收集回来的为:

3、学然后知不足。- 礼记
4、活到老学到老。-谚语
5、学习永远不晚。-高尔基
6、重复是学习之母。 - 狄慈根
C网站收集回来的为:
学然后知不足。
活到老学到老。
学习永远不晚。
重复是学习之母。

现在需要对以上合并出来的txt文档进行出来,最终只留下唯一的一行。(注:以上只是举例,真正复制回来的语录可能有几百上千条。)
作者: cjiabing    时间: 2014-4-10 10:45

这个复杂,实现起来有难度,只能具体问题具体分析投机取巧了
作者: CrLf    时间: 2014-4-10 10:53

本帖最后由 CrLf 于 2014-4-10 10:54 编辑

那你的规则是什么?

如果只以顶楼为例,那请用无敌 gawk,请自行下载
  1. gawk -F"[\d、|.| ]" "!a[$1]++" v.txt
复制代码

作者: kabamei    时间: 2014-4-10 10:58

http://blog.csdn.net/xlvector/article/details/2045054  

这里有个用别的语言处理的思路。 看用批处理能解决不。
作者: CrLf    时间: 2014-4-11 03:20

本帖最后由 CrLf 于 2014-4-14 08:33 编辑

已修改
搞定,楼主给分!
  1. @set @n=0;/*&echo off
  2. cscript /nologo /e:javascript %0 <输入.txt >输出.txt
  3. pause
  4. exit/b
  5. */
  6. var t = new Date().getTime();
  7. var i,j,m,n
  8. var count,bingo,rate,length
  9. var debug = false  //调试选项
  10. var format = true  //是否将过滤文本以提高准确度,如启用,下文参数皆以格式化后为准
  11. var step = 4   //分段长度数
  12. var text = WScript.StdIn.ReadAll()
  13. text = text.replace(/(?:\r?\n)+/mg,'\n')
  14. var ar = text.split(/\n/g)
  15. //分配对象属性
  16. for(i in ar){
  17. ar[i] = {str:ar[i],ar:ar[i]}
  18. if (format) {     //过滤文本
  19. ar[i].ar = ar[i].ar.replace(/[—:、;,。!?…~―(){}《》\x00-\x09\x0b\x0c\x0e-\x7f]+/g,' ')  //过滤标点
  20. ar[i].ar = ar[i].ar.replace(/(?:^| )[一二三四五六七八九十百]+(?:$| )/g,'')  //过滤数词
  21. ar[i].ar = ar[i].ar.replace(/[又很太着了的地得不]/g,'')  //过滤常见副词
  22. ar[i].ar = ar[i].ar.replace(/ +/g,' ')  //压缩重复符号
  23. ar[i].ar = ar[i].ar.replace(/^ | $/g,'')  //过滤首尾符号
  24. }
  25. }
  26. //过滤被包含行
  27. for(i=ar.length-1;i>0;i--){
  28. for(m=i-1;m>=0;m--){
  29. if(ar[m].ar.indexOf(ar[i].ar)!=-1||ar[i].ar.indexOf(ar[m].ar)!=-1){
  30. if(ar[i].ar.length>ar[m].ar.length)ar[m]=ar[i]
  31. ar.splice(i,1)
  32. break
  33. }
  34. }
  35. }
  36. //行分段
  37. for(i in ar){
  38. ar[i].ar = (
  39. str2ar(ar[i].ar,0,step).concat(
  40. str2ar(ar[i].ar,1,step),
  41. str2ar(ar[i].ar,2,step),
  42. str2ar(ar[i].ar,3,step)
  43. )
  44. )
  45. }
  46. for(i=ar.length-1;i>0;i--){
  47. for(m=i-1;m>=0;m--){
  48. bingo=bingo_max=count=0
  49. for(j in ar[i].ar){
  50. for(n in ar[m].ar){
  51. if(ar[i].ar[j]===ar[m].ar[n]){
  52. bingo++
  53. } else {
  54. count+=bingo
  55. bingo_max = Math.max(bingo_max,bingo)
  56. bingo = 0
  57. }
  58. }
  59. }
  60. length = Math.max(ar[i].ar.length,ar[m].ar.length)
  61. //过滤相似行
  62. count+=bingo
  63. if (count/length > Math.sin(2/(Math.sqrt(length+5)))) {
  64. if(ar[i].ar.length==ar[m].ar.length)ar[m]=ar[i]
  65. ar.splice(i,1)
  66. break
  67. }
  68. //过滤连续相似行
  69. bingo_max = Math.max(bingo_max,bingo)
  70. if (bingo_max/length > 1/Math.sqrt(length+1)) {
  71. if(ar[i].ar.length==ar[m].ar.length)ar[m]=ar[i]
  72. ar.splice(i,1)
  73. break
  74. }
  75. }
  76. }
  77. for(i in ar){
  78. WScript.Echo(ar[i].str)
  79. }
  80. if(debug)WScript.Echo(new Date().getTime()-t)
  81. function str2ar(str,i,step){
  82. var ar = new Array()
  83. for(;i<str.length;i+=step){
  84. ar.push(str.substr(i,step))
  85. }
  86. return ar
  87. }
复制代码

作者: CrLf    时间: 2014-4-11 03:25

本帖最后由 CrLf 于 2014-4-14 10:33 编辑

再来个套用现成 vbs 的 LD 算法,准确度好像不亚于 4 楼算法:
  1. Const input = "输入.txt"
  2. Const output = "输出.txt"
  3. Const isDebug = False
  4. Const length = 4
  5. t=Timer
  6. Set fso = CreateObject("Scripting.FileSystemObject")
  7. Set ts = fso.OpenTextFile(input,1)
  8. ar = Split(ts.ReadAll,vbCrLf)
  9. For i=UBound(ar) To 1 Step -1
  10. If Not test(ar,i) Then ar(i)=""
  11. Next
  12. Set ts = fso.CreateTextFile(output,1)
  13. For Each a In ar
  14. If Len(a) Then ts.WriteLine a
  15. Next
  16. If isDebug Then ts.WriteLine timer-t
  17. Function test(ar,i)
  18. Dim j,length
  19.     For j=0 To i-1
  20.         length = Len(ar(i))
  21.         If Len(ar(j))>Len(ar(i)) Then length = Len(ar(j))
  22.         If GetLevenshteinDistince(ar(i), ar(j)) > Sin(2/(Sqr(length)+5)) Then
  23.          test = False
  24. Exit Function
  25.         End If
  26.     Next
  27.     test = True
  28. End Function
  29. Function GetLevenshteinDistince(str1, str2)        '函数引用自:http://bbs.bathome.net/thread-27991-1-1.html
  30.         Dim x, y, A, B, C, K
  31.         Dim l1,l2
  32.         Dim Matrix()
  33.         l1 = l2 = 0
  34.         If Len(str2)>=length Then l1=Len(str2)-length+1
  35.         If Len(str1)>=length Then l2=Len(str1)-length+1
  36.         
  37.         ReDim Matrix(l2, l1)
  38.         
  39.         '初始化第一行和第一列
  40.         For x = 0 To UBound(Matrix, 1)
  41.                 Matrix(x, 0) = x
  42.         Next
  43.         For y = 0 To UBound(Matrix, 2)
  44.                 Matrix(0, y) = y
  45.         Next
  46.         '填充矩阵
  47.         For x = 1 To UBound(Matrix, 1)
  48.                 For y = 1 To UBound(Matrix, 2)
  49.                         If (Mid(str1, Matrix(0, y), 4) = Mid(str2, Matrix(x, 0), 4)) Then
  50.                                 C = Matrix(x -1 ,y - 1)
  51.                         Else
  52.                                 C = Matrix(x -1 ,y - 1) + 1
  53.                         End If
  54.                         
  55.                         A = Matrix(x - 1, y) + 1
  56.                         B = Matrix(x, y - 1) + 1
  57.                         
  58.                         If (A =< B and A =< C) Then Matrix(x, y) = A
  59.                         If (B =< C and B =< A) Then Matrix(x, y) = B
  60.                         If (C =< A and C =< B) Then Matrix(x, y) = C
  61.                 Next
  62.         Next
  63.         
  64.         '计算 LD 值
  65.         If (Len(str1) > Len(str2)) Then
  66.                 K = Len(str1)-length+1
  67.         Else
  68.                 K = Len(str2)-length+1
  69.         End If
  70.         GetLevenshteinDistince = 1 - (Matrix(l2, l1) / K)
  71. End Function
复制代码
函数引用自:http://bbs.bathome.net/thread-27991-1-1.html
作者: kabamei    时间: 2014-4-12 15:25

回复 6# CrLf

感谢版主,测试了下。 处理上面的绝对是没问题了。

但是处理我的文本 就不行了
作者: zhanglei1371    时间: 2014-4-12 20:57

Lz应该一开始就将目标文档上传上来,并提供处理好的样本。因为涉及到很多问题。删除上面重复的,还是下面重复的?文中多次重复段落出现时,要保留哪一个?这些都是很复杂的问题。
CrLf版主费了不少劲,结果白费劲了...
作者: CrLf    时间: 2014-4-14 07:04

回复 7# kabamei


    已修改,见 5 楼,原来错用了 pop 所以过滤不准确,测试遗漏的错误...
    判断前先过滤了一遍,把标点符号什么的替换为空,如果不需要,请自行注释。
    使用三种过滤逻辑,如果有哪一种不需要,请自行注释:
  1. 1、剔除完全被包含的行
  2. 2、剔除相似度超过阀值的行(算法:count/length = sin(2/(sqrt(length+5))))
  3. 3、剔除连续相似次数超过阀值的行(算法:count/length = 1/sqrt(length+1))
复制代码
只剔除较短的那行,如果不需要,请自行注释
    阀值算法为什么这么写,画一个函数图像你就懂了...
    楼主给的文本测试通过,至于准确度什么的,因为用了算法动态计算,所以没得微调,除非你自己换算法,不过不带语法解析功能的情况下,不可能判断得非常精确。
    优化后效率只能说凑合,本机测试楼主给的 14k 文本用时 23 秒,跟搜索引擎没得比。
    算是哥今年目前最棒的脚本了,傲娇...再不给分砸你家玻璃噢!
作者: kabamei    时间: 2014-4-14 23:15

回复 9# CrLf


    感谢了。 测试可以用。我好像只能加一分。
作者: CrLf    时间: 2014-4-15 12:47

回复 10# kabamei


    问题解决后,请编辑顶楼帖子在标题前面注明[已解决]
作者: terse    时间: 2014-4-15 12:59

这个真复杂
作者: CrLf    时间: 2014-4-15 14:31

回复 12# terse


    我做得复杂而已,其实用 js 实现 5 楼提供的思路,用标准格式大概 40 行左右就够了,但过滤得不是很干净
作者: zhanglei1371    时间: 2014-4-15 18:28

回复 13# CrLf


    也可以试试用word的vba来做,速度应该更快




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