Board logo

标题: [文本处理] [已解决]批处理如何删除前几个字符相同的行? [打印本页]

作者: lsxq    时间: 2013-5-10 17:50     标题: [已解决]批处理如何删除前几个字符相同的行?

本帖最后由 lsxq 于 2013-5-12 20:56 编辑

323.456.789.561
323.456.789.542
323.456.789.798
323.456.789.256

981.651.654.681
981.651.654.682

593.658.651.658

能否利用SED或者文本编辑器(类似EditPlus)删除“前几个字符相同的行”,行很多,前几个字符相同的行也很多。这里比如要删除前11个字符相同的所有行,

结果只剩下593.658.651.658这一行。
作者: apang    时间: 2013-5-10 20:12

sed不会,vbs练习中
  1. Set fso = CreateObject("Scripting.FileSystemObject")
  2. Set dic = CreateObject("Scripting.Dictionary")
  3. Set file  = fso.OpenTextFile("a.txt")
  4. Do Until file.AtEndOfStream
  5.    strLine = file.ReadLine
  6.    strKey = Left(strLine,11)
  7.    If Not dic.Exists(strKey) Then
  8.       dic.Add strKey,strLine
  9.    Else
  10.       dic.Item(strKey) = dic.Item(strKey) & vbLf & strLine
  11.    End If
  12. Loop
  13. For Each a in dic.Keys
  14.    If UBound(Split(dic.Item(a),vbLf)) = 0 Then
  15.       str = str & dic.Item(a) & vbCrLf
  16.    End If
  17. Next
  18. fso.CreateTextFile("b.txt",True).Write str
  19. MsgBox "OK"
复制代码

作者: xxpinqz    时间: 2013-5-10 21:11

sed也不会,vbs更不会,bat练习中...
  1. @echo off
  2. (for /f "delims= " %%a in (1.txt) do (
  3.   if not defined %%~na (
  4.     set ok=&set %%~na=a
  5.     call :sc "%%~na" "ok"
  6.     if not defined ok echo %%a
  7. )))>2.txt
  8. start 2.txt&exit /b
  9. :sc
  10. for /f "skip=1 delims=" %%a in ('findstr /b %1 1.txt 2^>nul') do set %~2=.
  11. goto :eof
复制代码

作者: pan528    时间: 2013-5-10 23:00

高手啊,三楼能解释一下代码吗,没有看懂。
作者: xxpinqz    时间: 2013-5-11 02:40

回复 4# pan528
不敢,新人啊,虽然这账号注册的很早~~
大体意思这样的:这个代码不通用
1、LZ要求11个字符(刚好是去除最后一个.???),所以用%%~na直接取
2、语句if not defined %%~na 和set %%~na=a 中的变量%%~na(即前11位)用来过滤重复行的,这个应该看的懂了,单单这样过滤出来的结果是不存在重复前11位的行
3、但是要求是有出现重复的就要去除,所以用 for /f "skip=1 delims=" %%a in ('findstr /b %1 1.txt 2^>nul') do set %~2=.来判断是否某个前11为字符的行有两行以上的,skip=1后直接取第二行,只要有值则认为有出现两次,则不输出.重复行不多的话:sc可以直接并入到第一个for中去,毕竟CALL比较慢。呃,发现少写了个goto :eof,应该是do set %~2=.&goto :eof,不用循环一遍。
4、delims= 等号后有个空格用来去除空格的行(论坛上好像说tokens=*可以去除首部的空格,按我理解应该也可以去除空格的行,不过我电脑上不行{WIN7},所以用这个),2^>nul则用来去除空行(就一回车换行的行)
5、文本如果超大,就没用了,变量定义有限制的,所以还是2楼的比较通用吧
改成这样应该会快点
  1. @echo off
  2. (for /f "delims= " %%a in (1.txt) do (
  3.   if not defined %%~na (
  4.     set _=&set %%~na=a
  5.     for /f "skip=1 delims=" %%a in ('findstr /b "%%~na" 1.txt 2^>nul') do set "_=_"
  6.     if not defined _ echo %%a
  7. )))>2.txt
复制代码

作者: pan528    时间: 2013-5-11 10:54

本帖最后由 pan528 于 2013-5-11 11:15 编辑

我觉得你的这段代码适用“删除倒数三个字符前相同的行”的情形,但第一行必须是重复行。
但是,我比较迟钝,还是没有看懂,代码是如何排除“倒数三个字符”的。请指教。
  1. :: 删除倒数三个字符前相同的行
  2. <"%~f0" more +11>1.txt
  3. @echo off
  4. (for /f "delims=-" %%a in (1.txt) do (
  5.   if not defined %%~na (
  6.     set ok=&set %%~na=a
  7.     for /f "skip=1 delims=" %%a in ('findstr /b "%%~na" 1.txt 2^>nul') do set "ok=-"
  8.     if not defined ok echo %%a
  9. )))>2.txt
  10. start 2.txt&exit /b
  11. 323.456.789.781.561
  12. 593.658.651.780.658
  13. 323.456.789.781.542
  14. 323.456.789.781.798
  15. 323.456.789.781.256
  16. 981.651.654.782.681
  17. 981.651.654.782.682
复制代码

作者: xxpinqz    时间: 2013-5-11 11:53

回复 6# pan528
呃,111.222.333.444你把这个设想为一个文件名%%~nxa,%%~na取文件名则为111.222.333(刚好11字符,符合LZ例子).444为扩展名%%~xa
实际上这代码相当于下面的缩减版
  1. @echo off&setlocal enabledelayedexpansion
  2. (for /f "delims= " %%a in (1.txt) do (
  3.   set ".=%%a"&set ".=!.:~,11!"
  4.   if not defined !.! (
  5. set _=&set !.!=a
  6. for /f "skip=1 delims=" %%a in ('findstr /b "!.!" 1.txt 2^>nul') do set "_=_"
  7. if not defined _ echo %%a
  8. )))>2.txt
  9. start 2.txt
复制代码
你那个因为%%~na是取到前15位,而非11位,才会出现你说的第一行必须要重复的。所以我才特意说明那是非通用版的,原因在此。要稍微通用则用这个,想取多少字符自己改数值11
作者: xxpinqz    时间: 2013-5-11 12:30

截取字符中不能含空格倒是真的,没处理,因为defined空格会出错。所以上面说明为稍微通用~~~~
作者: terse    时间: 2013-5-11 12:59

楼主的意思是把有重复项去掉 而不是记录一次
作者: apang    时间: 2013-5-11 13:02

回复 7# xxpinqz


    可以不用判断重复行的吧?重复行的话前面的11个字符也相同
  1. @echo off
  2. for /f "delims=" %%a in (1.txt) do (
  3.     set /a n=0
  4.     for /f "delims=" %%b in ('findstr /b "%%~na" 1.txt') do set /a n+=1
  5.     set /a "1/(n-1)" 2>nul||echo,%%a
  6. )
  7. pause
复制代码

作者: apang    时间: 2013-5-11 13:22

回复 10# apang


    sorry,理解错误,if not defined %%~na 是为了减少findstr搜索的次数
作者: terse    时间: 2013-5-11 14:51

文件大的话 用外部比较好
外部 GAWK
  1. gawk "{t=substr($0,0,11);n[t]++;a[t]=$0} END{for(m in n) if (n[m]<=1) print a[m]}" a.txt
复制代码
如钟情BAT 有点吃力 看文件大小
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "delims=" %%i in (a.txt) do (
  3.     set str=%%i&set "str=!str:~,11!"
  4.     if not defined #!str! (
  5.        set/a"n+=1"
  6.        set #!str!=#&set "$!n!=%%i"
  7.     ) else set "#!str!=$"
  8. )
  9. for /l %%i in (1 1 !n!) do (
  10.     set str=!$%%i!
  11.     for %%j in ("!str:~,11!") do if !#%%~j!# ==## echo !$%%i!
  12. )
  13. pause
复制代码
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "delims=" %%i in (a.txt) do (
  3.     set str=%%i&set "str=!str:~,11!"
  4.     set /a "$!str:~,11!+=1"
  5. )
  6. for /f "delims=" %%i in (a.txt) do (
  7.     set str=%%i
  8.     for %%j in ("!str:~,11!") do if "!$%%~j!" == "1" echo %%i
  9. )
  10. pause
复制代码

作者: xxpinqz    时间: 2013-5-11 16:29

回复 11# apang
是那个意思,这个set /a 很好使啊,学习了~
作者: xxpinqz    时间: 2013-5-11 16:45

12楼这代码,高手啊,快的不是那么一点点。
测了个8000行文本
2楼VBS代码大约在1s23左右,12楼第一个gawk代码5ms(不过会乱序了),第3个代码37s左右
小文本vbs和gawk相差无几.
至于5楼的代码嘛,可以直接忽略了,除非8000行前11位都一样的,这个运行时间才好意思写出来
学到很多~~谢谢2楼12楼
作者: terse    时间: 2013-5-11 21:09

回复 14# xxpinqz
难道测试数据不一样 差距这么大?
硬件也不会相差这么多吧
我这里测试 5W行数据都不过 5s 的
作者: xxpinqz    时间: 2013-5-11 23:45

回复 15# terse
测试文本
  1. (for /l %%a in (1000,1,9000) do echo 111.22.%%a.333)>1.txt
复制代码
我是用下面这个测的
  1. :: 别忘了在代码开头加一句set t=%time%
  2. call :time0 "%t%" "%time%" "ok"
  3. echo.&echo 耗时: %ok%
  4. pause&exit
  5. :time0  以下为封装部分。
  6. :: code 随风 @bbs.bathone.cn 2008-08-15
  7. setlocal&set /a n=0
  8. for /f "tokens=1-8 delims=.: " %%a in ("%~1:%~2") do (
  9.    set /a n+=10%%a%%100*360000+10%%b%%100*6000+10%%c%%100*100+10%%d%%100
  10.    set /a n-=10%%e%%100*360000+10%%f%%100*6000+10%%g%%100*100+10%%h%%100
  11. )
  12. set "n=%n:-=%"
  13. set /a s=n/360000,n=n%%360000,f=n/6000,n=n%%6000,m=n/100,n=n%%100
  14. set "ok=%s% 小时 %f% 分钟 %m% 秒 %n% 毫秒"
  15. endlocal&set "%~3=%ok%"&goto :eof
复制代码

作者: lsxq    时间: 2013-5-12 16:03

  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "delims=" %%i in (a.txt) do (
  3.     set str=%%i&set "str=!str:~,11!"
  4.     set /a "$!str:~,11!+=1"
  5. )
  6. for /f "delims=" %%i in (a.txt) do (
  7.     set str=%%i
  8.     for %%j in ("!str:~,11!") do if "!$%%~j!" == "1" echo %%i
  9. )
  10. pause
复制代码
  1. gawk "{t=substr($0,0,10);n[t]++;a[t]=$0} END{for(m in n) if (n[m]<=1) print a[m]}" a.txt
复制代码
额滴神啊,先谢谢大家了。这段代码的处理速度最快。也用GAWK测试了下,结果发现 文本的排序被打乱了。但是速度比 这段批处理 还要快。不知道这个能不能解决。

在这个需要处理的 文本中,这些数字不一定都是15位的。像下面这种情况:

323.456.789.561
323.456.789.542
323.456.789.798
323.456.789.256

981.651.654.681
981.651.654.682

456.55.45.11
456.55.45.2

47.66.15.123
47.66.15.113

593.658.651.658

可以想象成4段,只要是 前3段的数字相同 就删除。
作者: terse    时间: 2013-5-12 16:48

本帖最后由 terse 于 2013-5-12 17:28 编辑
额滴神啊,先谢谢大家了。这段代码的处理速度最快。也用GAWK测试了下,结果发现 文本的排序被打乱了。但是速 ...
lsxq 发表于 2013-5-12 16:03

如确认为固定四段 这样可以快点
虽然 扩展为文件名可以使代码更简练 但效率不一样 至少我这里是这样
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "tokens=1-3* delims=." %%i in (a.txt) do if defined $%%i.%%j.%%k (set $%%i.%%j.%%k=1) else set "$%%i.%%j.%%k=0"
  3. (for /f "tokens=1-3* delims=." %%i in (a.txt) do if "!$%%i.%%j.%%k!" == "0" echo %%i.%%j.%%k.%%l)>b.txt
  4. pause
复制代码

作者: terse    时间: 2013-5-12 17:49

本帖最后由 terse 于 2013-5-12 19:03 编辑

GAWK 文本打乱问题
刚刚的有问题
改为循环试
  1. gawk -F. "{c[NR]=$1 FS $2 FS $3;n[c[NR]]++;a[c[NR]]=$0} END{for(i=1;i<=NR;i++) if (n[c[i]]==1) print a[c[i]]}" a.txt
复制代码

作者: lsxq    时间: 2013-5-12 18:38

回复 19# terse


彻底服了,测试没有问题。真是太牛了。万分感谢啊。
作者: BAT-VBS    时间: 2013-5-12 20:03

回复 20# lsxq


    问题解决后,请编辑顶楼帖子在标题前面注明[已解决],并给回答者加分。
作者: lsxq    时间: 2013-5-12 20:56

回复 21# BAT-VBS

好的,谢谢提醒。




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