Board logo

标题: 【出题】批处理去除超大文本中的重复行 [打印本页]

作者: 随风    时间: 2009-5-29 14:11     标题: 【出题】批处理去除超大文本中的重复行

去除超大文本中的重复行
在非常批处理论坛看到这个题,觉得挺有挑战的,上百万行的数据,光靠批处理应该是难做到了,不知道借助第三方命令行工具能否完成。

文本内有大概100W行的数据
要求一、 删除所有重复行
要求二、 以----为分隔符,将第一列与第二列重复的行删除(描述有问题,以末尾的红色字更新为准)
两个要求完成一个也行,分两个代码完成也行,最完美的是在一个代码中完成两个要求。

部分样本
12779----alibaba140379----222.222.222.222----哈哈哈哈哈哈----2008-5-20----21:24:20
13166----BXGHgxst115----123.112.68.25----欧洲
12779----alibaba140379----203.93.208.138----山东省青岛市网通----2008-5-20----21:24:20
13166----BXGHgxst115----111.111.111.111
12779----alibaba140379----222.222.222.222----哈哈哈哈哈哈----2008-5-20----21:24:20
255004----dsfdsfdsf----123.112.77.25


扩展要求:保持原文本中不重复的行顺序不变

创建 100万 行左右的测试文件代码。
。。。
  1. @echo off&setlocal enabledelayedexpansion
  2. echo 正在创建测试文件 y1.txt 请稍候。。。 行数为 1000005 行
  3. (for /l %%a in (1 1 100) do echo !random!----!random!----!random!!random!----!random!)>y1.txt
  4. (for /l %%a in (1 1 10) do type y1.txt)>y2.txt
  5. (for /l %%a in (1 1 10) do type y2.txt)>y1.txt
  6. (for /l %%a in (1 1 10) do type y1.txt)>y2.txt
  7. (for /l %%a in (1 1 10) do type y2.txt)>y1.txt
  8. (echo !random!----!random!----!random!!random!----!random!!random!
  9. echo !random!----!random!----!random!!random!----!random!!random!
  10. echo !random!----!random!----!random!!random!----!random!!random!
  11. echo !random!----!random!----!random!!random!----!random!!random!
  12. echo !random!----!random!----!random!!random!----!random!!random!)>>y1.txt
  13. del /q y2.txt
复制代码
问题二描述有问题,现更新
要求二、 以----为分隔符,将第一列与第二列重复的行删除,保留第一行。
比如:
1234---555--44444444444
1234---7777--fafjaf
1234---555--444444
334----7898----dfadifaf
1234---555--00000000000000
这种情况下就保留第1、2、4行
因为第1行、第3行、第5行,的第1列和第2列是重复的。


[ 本帖最后由 随风 于 2009-5-29 17:59 编辑 ]
作者: Kiming    时间: 2009-5-29 15:20

就算写出来了 执行 也要卡死
可以用linux命令
作者: 随风    时间: 2009-5-29 15:24     标题: 回复 2楼 的帖子

就是要看能否突破这个效率问题啊^_^
作者: Batcher    时间: 2009-5-29 16:15

可能不是每个人都知道如何快速生成100W行测试文本,楼主在顶楼给个例子吧。
作者: 随风    时间: 2009-5-29 17:25

已在顶楼给出创建测试文件代码
作者: netbenton    时间: 2009-5-29 17:31

处理6W行记录花了2分多钟,100W就不敢试了!大概要一个多小时吧!
源文件为:sour2.txt
处理结果为:temp3.txt
可完成两个要求。
  1. @echo off&setlocal enabledelayedexpansion
  2. (for /f "delims=" %%a in (sour2.txt) do (
  3.         set/a n+=1
  4.         set num=000000!n!
  5.         echo !num:~-7!-%%a
  6. ))>temp.txt
  7. ::加入序号保持原来的顺序
  8. (for /f "tokens=1,2,3* delims=-" %%a in ('sort temp.txt /+8') do (
  9.         if "%%b-%%c" neq "!var!" (echo %%a-%%b-%%c-%%d)
  10.         set var=%%b-%%c
  11. ))>temp2.txt
  12. ::从第8位开始排序,即只按原来的数据排序,重复的丢弃
  13. (for /f "tokens=1,2,3* delims=-" %%a in ('sort temp2.txt') do (
  14. echo %%b----%%c----%%d
  15. ))>temp3.txt
  16. ::恢复原来的顺序,并丢弃加入的临时序号
复制代码
把楼主的数据存为sour.txt, 通过下面代码生成的sour2.txt具有6W行记录。
  1. @echo off
  2. (for /f "delims=" %%a in (sour.txt) do (
  3. for /l %%b in (1,1,10000) do (echo %%a)
  4. ))>sour2.txt
复制代码

[ 本帖最后由 netbenton 于 2009-5-29 17:51 编辑 ]
作者: 随风    时间: 2009-5-29 17:43

问题二描述有问题,现更新
作者: Batcher    时间: 2009-5-29 17:47

C:\Test>test.bat
0 小时 0 分钟 1 秒 51 毫秒

C:\Test>type test.bat
@echo off
set begin=%time%
gawk "!a[$0]++" y3.txt>>a.txt
call :time0 %begin% %time% duration
echo %duration%
goto :eof

:time0
::计算时间差(封装)
@echo off&setlocal&set /a n=0&rem code 随风 @bbs.bathome.net
for /f "tokens=1-8 delims=.: " %%a in ("%~1:%~2") do (
set /a n+=10%%a%%100*360000+10%%b%%100*6000+10%%c%%100*100+10%%d%%100
set /a n-=10%%e%%100*360000+10%%f%%100*6000+10%%g%%100*100+10%%h%%100)
set /a s=n/360000,n=n%%360000,f=n/6000,n=n%%6000,m=n/100,n=n%%100
set "ok=%s% 小时 %f% 分钟 %m% 秒 %n% 毫秒"
endlocal&set %~3=%ok:-=%&goto :EOF

作者: Kiming    时间: 2009-5-29 19:37     标题: 此方法应该可以的

@echo off
for /f %%i in (test.txt) do md %%i
dir/ad/b/o:n>Kiming.txt
for /f %%i in ('dir /b /ad') do rd %%i
是将里面的内容建立成目录 然后提取目录名 在写入新的txt
这个方法速度很快的哦
此方法思路源于 http://www.heycoffee.com/article/SoftwareWorks/320.htm
作者: 随风    时间: 2009-5-29 19:47

好恐怖的方法^_^
上百万行,你要创建多少个目录?况且如果文本内容含文件名不允许的字符怎办?
作者: Kiming    时间: 2009-5-29 19:53

大哥 md创建 很快的瞬间就好了的 不试试看怎么知道了
你给的那个100w行的代码有问题啊 就几个重复的
N多不重复的
至于那个非法字符用word搞下就可以了
好像里面没非法字符啊

[ 本帖最后由 Kiming 于 2009-5-29 19:54 编辑 ]
作者: 随风    时间: 2009-5-29 20:02

re 11 楼
你确定 那个100w行的代码有问题啊 就几个重复的 ?
作者: Kiming    时间: 2009-5-29 20:14     标题: 回LS的

我用了别人的代码测试了 发现只有 几行 所以我才认为有问题 实际上没问题
错怪了LZ了啊 对不起-_-!
作者: Kiming    时间: 2009-5-29 20:22

LZ我的代码我用了4分钟就解决了
本人亲自 测试 4分钟OK了 还算比较快吧
作者: 随风    时间: 2009-5-29 20:38     标题: 回复 14楼 的帖子

首先 4分钟 并不算快,
况且顶楼的只是临时测试样本,而非正在的需处理原件,为提高创建样本时间而产生的行大多是重复的,其实不重复的应该只有百来行,这使得你第二个for的运行时间大大减少,若不重复行占半数或更多呢?
其次,还是非法文件名问题,若在word中进行替换则使得代码变成了半自动,不够完美。
再次,创建这么多的临时文件,个人认为始终不太可取,如此疯狂的对硬盘进行写入操作不知道对硬盘会否有影响。
作者: Kiming    时间: 2009-5-29 20:42     标题: 这是VBT论坛的代码

@echo off
for /f "delims=" %%i in (test.txt) do (
    if not defined %%i set %%i=s & echo %%i>>result.txt)
大概需要30 s就可以了
可是这段代码我有点不理解 不知LS的道能否解释下
作者: 随风    时间: 2009-5-29 20:54

30s就可以遍历百万行的文本?没测试过,但对这类代码的评论,你没看到他8楼的回复吗?
作者: Kiming    时间: 2009-5-29 20:59

真的是30s啊
我测试了的 你测试没
那个8L的说有问题 我怎么没测试出来呢
作者: Batcher    时间: 2009-5-29 21:07     标题: 回复 9楼 的帖子

如果只有几个重复行,那就意味着要创建100多万个文件夹,这对于使用FAT32的朋友来说,是几乎无法实现的。
作者: Kiming    时间: 2009-5-29 21:16     标题: 回LS的啊

那16L;的 不是已经给出了代码了吗 可以了的 我还测试了
作者: Batcher    时间: 2009-5-29 21:19     标题: 回复 20楼 的帖子

我说的是9楼的代码,你确定自己用的FAT32么?
作者: Batcher    时间: 2009-5-29 21:22     标题: 回复 16楼 的帖子

批处理是不允许无限制的定义变量的,这个代码能够成功只是个例。如果随风把顶楼生成测试文本的脚本改改,让每行长度增加一倍,这时生成的文件将接近100MB,你再试试那个方案还能成功否?

参考:http://bbs.bathome.net/viewthread.php?tid=3403#pid21404
作者: Kiming    时间: 2009-5-29 21:23     标题: 回21L的

我是NTFS的 那个测试的代码生成的只有100多个文件夹
作者: Batcher    时间: 2009-5-29 21:25     标题: 回复 23楼 的帖子

19楼说的不够清楚吗?没关系,咱们再强调一遍吧:
如果只有几个重复行,那就意味着要创建100多万个文件夹,这对于使用FAT32的朋友来说,是几乎无法实现的。

作者: Kiming    时间: 2009-5-29 21:30     标题: 回复 24楼 的帖子

只有100W行啊  100MB的文件估计也有500W行了 只要求100w的文件
作者: Batcher    时间: 2009-5-29 21:48

楼主,能否稍微改改你生成测试文本的代码,要求的效果是100多W行文本,文件体积超过100MB,给感兴趣的朋友们看看?
作者: netbenton    时间: 2009-5-29 22:19

一个cmd最大可以使用内存为64M,除去一些系统和BAT程序本身占用,应该有60M可以给变量使用,所有字符是以字为单元,一变量定义就算只有:一个字变量名,一个等号,一个字符值,一个分隔符,最少要占4个字的空间,也就是说以最小的变量定义计算,只可以定义:60/2/4=7.5M个变量
如果这100W行中只有少数几行是重复的话,就要定义近100W个变量,相当一1M个变量,可是如果定义的变量名平均超过28个字符的话,无疑就会溢出。就此题来说用定义变量判断重复的方法,出错的可能性有一半。
还有一点,当定义的变量数量过大时,系统去搜索变量名也是需要时间的。
我曾经做过这种测试,就是不停的定义变量,看一个cmd到底能定义多少个变量。
最后是定义到该cmd进程序占用40多M内存时就不耐烦了,等待了好长时间,越是到最后就越慢。
作者: plp626    时间: 2009-5-30 03:31

如果要处理的字符没有双引号(有双引号时借助sed先用特殊字符比如中文下的特殊字符,这个花不了到少时间)
新打开一个命令行然后:
for /f "eol= delim=" %a in (tmp.txt)do @if not defined "%a" (@echo.%a>>your.txt&set "%%a=1")
echo.  >>file 的效率大概是每秒5000次,那么100万行需要200秒左右
作者: plp626    时间: 2009-5-30 03:38

原帖由 Kiming 于 2009-5-29 20:42 发表
@echo off
for /f "delims=" %%i in (test.txt) do (
    if not defined %%i set %%i=s & echo %%i>>result.txt)
大概需要30 s就可以了
可是这段代码我有点不理解 不知LS的道能否解释下


  if not defined %%i set %%i=s
这句就是保证单行输出的
当一个行第一次出现时,输出后给他一个赋值(随便什么数字都型),当下次它再次出现时,因为有上次的set ...=s
所以已经是"defined"了,自然后面的echo %%i>>result.txt就不会执行了

你把 if not defined %%i set %%i=s & echo %%i>>result.txt改成

if not defined %%i (echo %%i>>result.txt&set %%i=s ) 就容易理解了(注意加上括号)

但是这个代码有bug的,上楼就没有bug了
对于开头行是;还有行内含有空格,特殊字符的<>|等的行
for /f "eol= delim=" %a in (tmp.txt)do @if not defined "%a" @(echo.%a>>your.txt&set "%a=1")
都可以解决

[ 本帖最后由 plp626 于 2009-5-30 03:42 编辑 ]
作者: Kiming    时间: 2009-5-30 07:16     标题: 回复 29楼 的帖子

为什么要 set %%i=s 就不能等于别的吗
“delims=" 这是什么意思啊 delims 是想删除什么啊
作者: terse    时间: 2009-5-30 13:29

第三方工具 有谁测试下吗 我这里貌似可以的
  1. gawk "BEGIN {FS=\"-\"} !a[$1 $2]++" y1.TXT>y2.txt
复制代码
测试代码这样的
  1. @echo off&setlocal enabledelayedexpansion
  2. echo 正在创建测试文件 y1.txt 请稍候
  3. for /l %%a in (1 1 1000) do (
  4. (for /l %%a in (1 1 2001) do echo !random!!random!!random!----!random!!random!!random!----!random!----!random!!)>>y1.txt
  5. (for /l %%a in (1 1 1001) do echo %random%----%random%----!random!!random!!random!----!random!!random!!random!)>>y1.txt
  6. )
复制代码
生成150MB左右的Y1文件
处理后 Y2文件 近100MB 左右
处理时间 13.5秒

[ 本帖最后由 terse 于 2009-5-30 13:30 编辑 ]
作者: Batcher    时间: 2009-5-30 13:43     标题: 回复 30楼 的帖子

1、随便等于什么都可以,但是为了减少内存占用量,越短越好。

2、批处理for语句中tokens=*和delims=的区别是什么?
http://bbs.bathome.net/viewthread.php?tid=1654#pid8870
作者: plp626    时间: 2009-5-30 21:41

原帖由 Kiming 于 2009-5-30 07:16 发表
为什么要 set %%i=s 就不能等于别的吗
“delims=" 这是什么意思啊 delims 是想删除什么啊


可以等于别的,比如set %%i=他妈
也可以,只要不是空就行

delims=这个你看下for名的帮助信息

另外既然是超大文本行,批处理效率一定不会多高,借助三方工具。。。
这里有个帖子看看:
http://www.linuxsir.org/bbs/showthread.php?t=132848
作者: zqz0012005    时间: 2009-6-2 14:39

这个只能用第三方工具了。
正则表达式都不好使。
作者: kevinpeng    时间: 2009-8-15 14:44

  1. @echo off
  2. for /f "delims=" %%i in (test.txt) do (
  3.     if not defined %%i set %%i=s & echo %%i>>result.txt)
复制代码

我是一个初学者,对上面一段代码我整整想了两三天,搞得我上班都没心情!
为了学好批处理,不上班的时候我常常在网吧一坐就是一整天(无奈几个月前买的联想本本用了不到一个月就被偷了,上班又不是上网)!
现在看到这贴总算心理舒服多了!
作者: asnahu    时间: 2009-9-20 00:50

看来这个帖子要沉了,  zqz0012005是VBS高手,用VBS能否实现?
作者: zqz0012005    时间: 2010-6-19 02:47

微软 脚本专家
如何从文本文件中删除所有重复行?
http://www.microsoft.com/china/t ... rces/hey050819.mspx
http://msdn.microsoft.com/en-us/library/ms974559
  1. Const adOpenStatic = 3
  2. Const adLockOptimistic = 3
  3. Const adCmdText = &H0001
  4. Set objConnection = CreateObject("ADODB.Connection")
  5. Set objRecordSet = CreateObject("ADODB.Recordset")
  6. strPathToTextFile = "C:\Scripts\"
  7. strFile = "Test.txt"
  8. objConnection.Open "Provider=Microsoft.Jet.OLEDB.4.0;" & _
  9.       "Data Source=" & strPathtoTextFile & ";" & _
  10.           "Extended Properties=""text;HDR=NO;FMT=Delimited"""
  11. objRecordSet.Open "Select DISTINCT * FROM " & strFile, _
  12.     objConnection, adOpenStatic, adLockOptimistic, adCmdText
  13. Do Until objRecordSet.EOF
  14.     Wscript.Echo objRecordSet.Fields.Item(0).Value   
  15.     objRecordSet.MoveNext
  16. Loop
复制代码
速度还不错,但文本不能太复杂,因为有FMT=Delimited

或者通过Schema.ini文件指定按固定长度(但长度多少才算合适?)
作者: wc726842270    时间: 2010-9-17 00:22

这个...这个。我怎么一句也没看明白啊,好像是其它的脚本吧(我的英文水平太次,无法理解),这就是VBS 么?
作者: Batcher    时间: 2010-9-17 09:01     标题: 回复 38楼 的帖子

找几个VBS基础教程看看吧
作者: 0428_hai    时间: 2011-4-26 18:09

挖坟出来,研究研究。。。
作者: CrLf    时间: 2011-4-27 13:31

外部命令的威力在处理超大文本是彰显无遗:
  1. @echo off&setlocal enabledelayedexpansion
  2. (for /f "delims=" %%a in (y1.txt) do (
  3. set /a "n=(10000001+n)-(n/10000000)*10000000"
  4. echo !n:~-7!:%%a
  5. ))>1.tmp
  6. sort /rec 8198 /+8 1.tmp>2.tmp
  7. (for /f "tokens=1* delims=:" %%a in (2.tmp) do (
  8. if "%%b" neq "!l!" (
  9. echo %%a
  10. set l=%%b
  11. )
  12. ))>3.tmp
  13. findstr /b /g:3.tmp 1.tmp>4.tmp
  14. (for /f "tokens=1* delims=:" %%a in (4.tmp) do echo %%b)>y2.txt
  15. del /f /q ?.tmp
  16. (for /f "delims=" %%a in (y2.txt) do (
  17. set /p=%%a:<nul
  18. for /f "tokens=3" %%b in ('find /c "%%a" y1.txt') do echo 存在%%b行重复
  19. ))>check.txt
  20. pause
复制代码
未测试速度,但是不考虑对特殊字符的兼容性的情况下,这应该比if defined快
作者: CrLf    时间: 2011-4-27 14:05

本帖最后由 zm900612 于 2011-4-27 14:08 编辑

发现一个问题,为什么测试文件y1.txt中重复行这么多,测试了下,每行都有1000行相同...剔除重复行以后只剩下105行,需要定义的变量很少,难怪if defined的办法没怎么受影响,竟然只要二十多秒...无语了
作者: qzwqzw    时间: 2011-4-27 17:55

发现一个问题,为什么测试文件y1.txt中重复行这么多,测试了下,每行都有1000行相同...剔除重复行以后只剩下105行,需要定义的变量很少,难怪if defined的办法没怎么受影响,竟然只要二十多秒...无语了
zm900612 发表于 2011-4-27 14:05

那当然
测试代码只随机生成了100行文本
然后就通过四级for循环type复制到了1000000行
最后再加了五行随机文本
这样的样本代表性太弱了
作者: CrLf    时间: 2011-4-27 18:11

哦,没注意,我还以为和随机数种子有关呢...
作者: qzwqzw    时间: 2011-4-27 19:15

随机种子只决定了随机数序列的一致性
而随机数的散列情况取决于随机算法

也就是说
同一个种子同一种算法
仍然会生成无穷个随机数
组成一个序列
而这个随机数序列总是一致的

另外
以上讨论的都是“伪随机”
计算机的组成原理决定了
不可能有绝对的随机算法存在
我们只是无限逼近随机
大多数情况下这种随机散列就足够了

当然
顶楼的算法连“伪随机”也算不上了
作者: CrLf    时间: 2011-4-27 19:31

复制一楼代码的时候因为感觉创建类似的测试文本的可选方案很少,扫一眼没看到有特色的东西,所以就没细看…太懒啊太懒啊
作者: garyng    时间: 2011-8-20 17:25

回复 1# 随风

汗~
100W行的txt有35MB啊~
要开都卡死了~
作者: xudaweb    时间: 2011-8-26 17:37

是啊,几十M,本想打开一看究竟,随手一双击,进程就无响应了。。。
作者: zhsh1128    时间: 2016-5-24 17:05

这个问题有答案么?我非常非常需要。我的工作中常常需要剔除几十万行数据中重复的行。目前的方法是:在excel中用数组+字典,速度也是超快。我想用批处理来处理文本数据,一直不知道怎么入手。
作者: codegay    时间: 2016-5-24 17:27

回复 49# zhsh1128


    学门高级语言吧。入门门槛会高点,但是熟练以后自由度会高很多,很多东西都能自己做。
python ruby powershell之类的语言都可以。
作者: happy886rr    时间: 2016-5-24 18:09

回复 49# zhsh1128
赞同楼上观点,尤其是C语言或者汇编速度快的惊人,可以pcre正则库,或者hash值效验,perl善于文本处理,python易于上手。
作者: codegay    时间: 2016-5-24 19:09

本帖最后由 codegay 于 2016-5-24 19:41 编辑

回复 51# happy886rr


    ruby互联网新兴创业公司有在用,
python在数据处理之类的领域也有用处,
powershell在windows平台上做运维自动化有优势,
正则库之类的文本处理库,其它语言都支持了,perl已经没有什么的优势了。

能把程序写出来,才能走到考虑性能的那一步。
作者: happy886rr    时间: 2016-5-24 19:50

回复 52# codegay
go语言也不错,就是不怎么火。py非常简洁,但速度太慢。julia数学功能很全,但是不如纯C快。做计算最好C来完成,C也可以用数组实现大数运算。但是在非数学计算方面,我非常喜欢用py,写的很少很简洁,各种模块很多,就是在搭积木。Bat是无法取代的胶水语言,用来衔接win下的各种程序,就是没找到cmd、find之类的源码,微软不开源吗。
作者: codegay    时间: 2016-5-24 20:31

回复 53# happy886rr


    go 发展得也还很不错,一些新公司也有在用GO的,有GOOGLE在推广。好像编译后只生成一个可执行文件。也是很方便的一优点。
我对GO有也有兴趣。
作者: 我来了    时间: 2016-9-5 21:51

回复 4# Batcher

这个简单,用w32dasm 输出汇编代码,一般每个文件 都在几十M以上,大的百M以上不稀奇。
通常大部分编辑器会卡死,但gvim还是很厉害的




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