标题: [文本处理] 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,请自行下载- 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 编辑
已修改
搞定,楼主给分!- @set @n=0;/*&echo off
- cscript /nologo /e:javascript %0 <输入.txt >输出.txt
- pause
- exit/b
- */
- var t = new Date().getTime();
- var i,j,m,n
- var count,bingo,rate,length
-
- var debug = false //调试选项
-
- var format = true //是否将过滤文本以提高准确度,如启用,下文参数皆以格式化后为准
- var step = 4 //分段长度数
-
- var text = WScript.StdIn.ReadAll()
-
- text = text.replace(/(?:\r?\n)+/mg,'\n')
- var ar = text.split(/\n/g)
-
- //分配对象属性
- for(i in ar){
- ar[i] = {str:ar[i],ar:ar[i]}
- if (format) { //过滤文本
- ar[i].ar = ar[i].ar.replace(/[—:、;,。!?…~―(){}《》\x00-\x09\x0b\x0c\x0e-\x7f]+/g,' ') //过滤标点
- ar[i].ar = ar[i].ar.replace(/(?:^| )[一二三四五六七八九十百]+(?:$| )/g,'') //过滤数词
- ar[i].ar = ar[i].ar.replace(/[又很太着了的地得不]/g,'') //过滤常见副词
- ar[i].ar = ar[i].ar.replace(/ +/g,' ') //压缩重复符号
- ar[i].ar = ar[i].ar.replace(/^ | $/g,'') //过滤首尾符号
- }
- }
-
- //过滤被包含行
- for(i=ar.length-1;i>0;i--){
- for(m=i-1;m>=0;m--){
- if(ar[m].ar.indexOf(ar[i].ar)!=-1||ar[i].ar.indexOf(ar[m].ar)!=-1){
- if(ar[i].ar.length>ar[m].ar.length)ar[m]=ar[i]
- ar.splice(i,1)
- break
- }
- }
- }
-
- //行分段
- for(i in ar){
- ar[i].ar = (
- str2ar(ar[i].ar,0,step).concat(
- str2ar(ar[i].ar,1,step),
- str2ar(ar[i].ar,2,step),
- str2ar(ar[i].ar,3,step)
- )
- )
- }
-
- for(i=ar.length-1;i>0;i--){
- for(m=i-1;m>=0;m--){
- bingo=bingo_max=count=0
- for(j in ar[i].ar){
- for(n in ar[m].ar){
- if(ar[i].ar[j]===ar[m].ar[n]){
- bingo++
- } else {
- count+=bingo
- bingo_max = Math.max(bingo_max,bingo)
- bingo = 0
- }
- }
- }
- length = Math.max(ar[i].ar.length,ar[m].ar.length)
-
- //过滤相似行
- count+=bingo
- if (count/length > Math.sin(2/(Math.sqrt(length+5)))) {
- if(ar[i].ar.length==ar[m].ar.length)ar[m]=ar[i]
- ar.splice(i,1)
- break
- }
-
- //过滤连续相似行
- bingo_max = Math.max(bingo_max,bingo)
- if (bingo_max/length > 1/Math.sqrt(length+1)) {
- if(ar[i].ar.length==ar[m].ar.length)ar[m]=ar[i]
- ar.splice(i,1)
- break
- }
- }
- }
-
- for(i in ar){
- WScript.Echo(ar[i].str)
- }
-
- if(debug)WScript.Echo(new Date().getTime()-t)
-
- function str2ar(str,i,step){
- var ar = new Array()
- for(;i<str.length;i+=step){
- ar.push(str.substr(i,step))
- }
- return ar
- }
复制代码
作者: CrLf 时间: 2014-4-11 03:25
本帖最后由 CrLf 于 2014-4-14 10:33 编辑
再来个套用现成 vbs 的 LD 算法,准确度好像不亚于 4 楼算法:- Const input = "输入.txt"
- Const output = "输出.txt"
- Const isDebug = False
- Const length = 4
-
- t=Timer
-
- Set fso = CreateObject("Scripting.FileSystemObject")
- Set ts = fso.OpenTextFile(input,1)
-
- ar = Split(ts.ReadAll,vbCrLf)
-
- For i=UBound(ar) To 1 Step -1
- If Not test(ar,i) Then ar(i)=""
- Next
-
- Set ts = fso.CreateTextFile(output,1)
-
- For Each a In ar
- If Len(a) Then ts.WriteLine a
- Next
-
- If isDebug Then ts.WriteLine timer-t
-
- Function test(ar,i)
- Dim j,length
-
- For j=0 To i-1
- length = Len(ar(i))
- If Len(ar(j))>Len(ar(i)) Then length = Len(ar(j))
- If GetLevenshteinDistince(ar(i), ar(j)) > Sin(2/(Sqr(length)+5)) Then
- test = False
- Exit Function
- End If
- Next
- test = True
- End Function
-
- Function GetLevenshteinDistince(str1, str2) '函数引用自:http://bbs.bathome.net/thread-27991-1-1.html
- Dim x, y, A, B, C, K
- Dim l1,l2
- Dim Matrix()
- l1 = l2 = 0
- If Len(str2)>=length Then l1=Len(str2)-length+1
- If Len(str1)>=length Then l2=Len(str1)-length+1
-
- ReDim Matrix(l2, l1)
-
- '初始化第一行和第一列
- For x = 0 To UBound(Matrix, 1)
- Matrix(x, 0) = x
- Next
- For y = 0 To UBound(Matrix, 2)
- Matrix(0, y) = y
- Next
-
- '填充矩阵
- For x = 1 To UBound(Matrix, 1)
- For y = 1 To UBound(Matrix, 2)
- If (Mid(str1, Matrix(0, y), 4) = Mid(str2, Matrix(x, 0), 4)) Then
- C = Matrix(x -1 ,y - 1)
- Else
- C = Matrix(x -1 ,y - 1) + 1
- End If
-
- A = Matrix(x - 1, y) + 1
- B = Matrix(x, y - 1) + 1
-
- If (A =< B and A =< C) Then Matrix(x, y) = A
- If (B =< C and B =< A) Then Matrix(x, y) = B
- If (C =< A and C =< B) Then Matrix(x, y) = C
- Next
- Next
-
- '计算 LD 值
- If (Len(str1) > Len(str2)) Then
- K = Len(str1)-length+1
- Else
- K = Len(str2)-length+1
- End If
-
- GetLevenshteinDistince = 1 - (Matrix(l2, l1) / K)
- 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、剔除完全被包含的行
- 2、剔除相似度超过阀值的行(算法:count/length = sin(2/(sqrt(length+5))))
- 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 |