Board logo

标题: [文本处理] 批处理如何将txt文本里的所有重复行排到文本前端? [打印本页]

作者: a4620269    时间: 2009-2-27 19:07     标题: 批处理如何将txt文本里的所有重复行排到文本前端?

本帖最后由 pcl_test 于 2016-9-6 19:47 编辑

txt 文本排版批处理求救(上万行的)
例如文本内容是:
1111
2222
3333
3333
4444
5555
4444


我想把重复的文本排在所有文本的前面,得到新的文本文件如下(不用做排序,乱的就行,只要在前面):
3333
3333
4444
4444
1111
2222
5555

求教,感激不尽万行

[ 本帖最后由 a4620269 于 2009-2-27 20:13 编辑 ]
作者: batman    时间: 2009-2-27 19:33

本代码可适用于多次重复的情况
  1. @echo off&setlocal enabledelayedexpansion
  2. for /f "delims=" %%a in (a.txt) do set /a _%%a+=1
  3. for /f "delims=_=" %%a in ('set _') do (
  4.     if !_%%a! neq 1 (
  5.        for /l %%b in (1,1,!_%%a!) do echo %%a
  6.        ) else (
  7.        set /a n+=1&set "_!n!=%%a"
  8.     )
  9. )
  10. for /l %%a in (1,1,!n!) do echo !_%%a!
  11. pause>nul
复制代码

[ 本帖最后由 batman 于 2009-2-27 20:25 编辑 ]
作者: a4620269    时间: 2009-2-27 19:38     标题: 回复 2楼 的帖子

太厉害了,但是我得不到bat 里的文本,怎么办
作者: batman    时间: 2009-2-27 19:40

原帖由 a4620269 于 2009-2-27 19:38 发表
太厉害了,但是我得不到bat 里的文本,怎么办

把 echo *改成echo *>>b.txt
作者: a4620269    时间: 2009-2-27 19:47     标题: 回复 4楼 的帖子

有三个  echo  我不知改哪个
能不能来个全的
作者: lhjoanna    时间: 2009-2-27 19:53

除了@echo off中的echo,都改为echo *>>tmp.txt
作者: terse    时间: 2009-2-27 20:02

Re: batman兄
一个  set/a "_%%a+=1 就够了哦
作者: a4620269    时间: 2009-2-27 20:09

处理几十行没问题,我这上万行的文件一开bat 就没反映了,有什么好的办法,我电脑配置也不低,3G内存
作者: Batcher    时间: 2009-2-27 20:11     标题: 回复 8楼 的帖子

建议在顶楼更新帖子,说明需要处理“万行的文件”,以便他人针对你的具体情况给出合适的方案。
作者: batman    时间: 2009-2-27 20:26

原帖由 terse 于 2009-2-27 20:02 发表
Re: batman兄
一个  set/a "_%%a+=1 就够了哦

兄所言即是,已修改,写代码时未及细作,惭愧。。。。
作者: a4620269    时间: 2009-2-27 20:31     标题: 回复 10楼 的帖子

那么新的代码是什么呢
作者: namejm    时间: 2009-2-27 23:03

  思路:先用sort把文本排序,这样,重复的行就前后相连,然后,用for来读取文本,判断上下句是否一致即可。
作者: Batcher    时间: 2009-2-28 00:16

  1. @echo off
  2. echo 一万行文本的测试结果
  3. echo 开始时间%time%
  4. sort a.txt>b.txt
  5. (for /f "delims=" %%a in (b.txt) do (
  6.   if not defined %%a (
  7.     set "%%a=1"
  8.   ) else (
  9.     echo.%%a
  10.   )
  11. ))>c.txt
  12. findstr /x /g:c.txt a.txt>d.txt
  13. findstr /x /v /g:c.txt a.txt>>d.txt
  14. echo 结束时间%time%
  15. pause
复制代码
一万行文本的测试结果
开始时间 0:16:46.42
结束时间 0:16:55.21
Press any key to continue . . .

作者: terse    时间: 2009-2-28 00:37

假设要处理的文件为a.txt 运行前备份
  1. 啊  有了啊  竟然一致
复制代码

另:
是否加个判断c.txt的生成呢 空就跳过FINDSTR 不然报错

[ 本帖最后由 terse 于 2009-2-28 00:47 编辑 ]
作者: Batcher    时间: 2009-2-28 10:40     标题: 回复 14楼 的帖子

感谢提醒。
没有重复行的时候,c.txt依然会生成,只不过为空而已。
干脆屏蔽算了。
  1. @echo off
  2. echo 一万行文本的测试结果
  3. echo 开始时间%time%
  4. sort a.txt>b.txt
  5. (for /f "delims=" %%a in (b.txt) do (
  6.   if not defined %%a (
  7.     set "%%a=1"
  8.   ) else (
  9.     echo.%%a
  10.   )
  11. ))>c.txt
  12. findstr /x /g:c.txt a.txt>d.txt 2>nul
  13. findstr /x /v /g:c.txt a.txt>>d.txt 2>nul
  14. echo 结束时间%time%
  15. pause
复制代码

作者: namejm    时间: 2009-2-28 12:20

  把重复的行内容作为变量名不失为一种思路,但是会有如下缺陷:
  1、如果重复内容过多,势必会有大量的变量产生,占用的内存将飙升——虽然目前上G的内存已成主流,但是,这上G的内存不应该只被自己的程序使用,而应该留出足够的内存给其他程序运行,大量命名变量乃编程大忌,当然,如果你只求结果而不管过程,或者并没有做程序员的想法,则可以忽略这一条;
  2、变量名有自己的一套规则,有些字符是不能作为变量名的,比如&、|之类的特殊字符;如果要处理的文本中含有这些字符,则batcher的代码会出现问题——虽然在设置变量的时候用引号把 set 语句的部分内容括起来了,但是在用 if defined 语句判断变量名是否已经赋值的时候仍然会出错;(备注:此条表述有误,请看下一楼层zqz0012005的描述)
  
  用顶楼的数据测试batcher的代码,发现4444这两行内容并没有在一起,原因有二:
  1、findstr /x 做整句匹配的时候,是以行尾是否带回车换行符号作为判断依据的,而最后一行内容没有带回车换行符号,所以,findstr /x 不认为它是完整的一行内容,从而被忽略掉——我们常用 findstr /v $ 来提取最后一行的内容,也是基于这个原理;
  2、findstr /g:c.txt a.txt 的时候,获取到的内容将以后一个文本内容的顺序提取,而不是第一个文本内容的顺序;也就是说,假如111、222分别位于c.txt的第一、二行,分别位于a.txt的第二、一行,那么,提取到的内容将是先222后111。这会产生什么后果呢?如果在顶楼给出的数据中,第一行之前插入5555,得出的结果中,5555并没有在一起。

  要怎么解决这两个问题呢?一个简洁的办法是:先用sort对原始数据排序,然后,用for来读取文本,判断上下句是否一致,从而提取出重复的行内容;findstr /x 的时候,以排序后的内容作为查找对象,而不是以原始文本作为对象。代码如下(此代码的执行结果会对数据进行排序,出于效率考虑,没有兼容感叹号):
  1. @echo off
  2. echo 一万行文本的测试结果
  3. echo 开始时间%time%
  4. sort a.txt>b.txt
  5. cd.>c.txt
  6. setlocal enabledelayedexpansion
  7. for /f "delims=" %%i in (b.txt) do (
  8.     if "!str!"=="%%i" (echo !str!)>>c.txt
  9.     set "str=%%i"
  10. )
  11. for %%i in (c.txt) do if "%%~zi"=="0" goto end
  12. findstr /x /g:c.txt b.txt>d.txt
  13. findstr /x /v /g:c.txt b.txt>>d.txt
  14. start d.txt
  15. :end
  16. echo 结束时间%time%
  17. pause
复制代码

作者: zqz0012005    时间: 2009-2-28 14:47     标题: 回复 16楼 的帖子

把重复的行内容作为变量名,第二个缺陷其实不存在。
特殊字符在设置变量的时候用引号把 set 语句的部分内容括起来了,固然应该这样做;而在用 if defined 检测变量时,利用for的中间变量:if defined %%i,同样不会因为特殊字符而出错。
作者: namejm    时间: 2009-2-28 14:59

  嗯,用 if defined %%i 的格式确实不会因为特殊字符而出错,看来是我搞错了。
作者: Batcher    时间: 2009-2-28 15:13     标题: 回复 16楼 的帖子

1、如果重复内容过多,势必会有大量的变量产生,占用的内存将飙升——虽然目前上G的内存已成主流,但是,这上G的内存不应该只被自己的程序使用,而应该留出足够的内存给其他程序运行,大量命名变量乃编程大忌,当然,如果你只求结果而不管过程,或者并没有做程序员的想法,则可以忽略这一条;

这一条是不能忽略的,因为批处理是不允许无限制地定义变量的。
http://technet.microsoft.com/en-us/library/bb490954.aspx
The maximum individual environment variable size is 8192bytes.
The maximum total environment variable size for all variables, which includes variable names and the equal sign, is 65,536KB.

用顶楼的数据测试batcher的代码,发现4444这两行内容并没有在一起,原因有二:

感谢提醒,代码更改如下:
  1. @echo off
  2. echo 一万行文本的测试结果
  3. echo 开始时间%time%
  4. cd.>>a.txt
  5. sort a.txt>b.txt
  6. (for /f "delims=" %%a in (b.txt) do (
  7.   if not defined %%a (
  8.     set "%%a=1"
  9.   ) else (
  10.     echo.%%a
  11.   )
  12. ))>c.txt
  13. findstr /x /g:c.txt b.txt>d.txt 2>nul
  14. findstr /x /v /g:c.txt b.txt>>d.txt 2>nul
  15. echo 结束时间%time%
  16. pause
复制代码

作者: zqz0012005    时间: 2009-2-28 15:24

jm的代码应该是最合适的。

关于最后一行不是以回车换行符结束的问题,可以用more读文本内容再用管道传递给findstr(虽然用管道会降低速度)。

以前用more时无意中发现它会自动在最后加一个回车换行符。开始不知道more的这一特点,导致我当时的一个代码测试很久都不成功,所以对这一点印象很深刻。

2、findstr /g:c.txt a.txt 的时候,获取到的内容将以后一个文本内容的顺序提取,而不是第一个文本内容的顺序。
的确,如果不先给a.txt排序的话,findstr /g:c.txt a.txt 这一句虽然提取出重复行排在文本前面,但还是无法让它们相邻(它们仍是按原来的次序排列)。

[ 本帖最后由 zqz0012005 于 2009-2-28 15:26 编辑 ]
作者: pcl_test    时间: 2016-9-6 22:22

  1. //&cls&cscript -nologo -e:jscript "%~f0"&pause&exit
  2. var fso = new ActiveXObject('Scripting.FileSystemObject');
  3. var file = '文本.txt';
  4. var a={};
  5. var f = fso.OpenTextFile(file, 1);
  6. while(!f.AtEndOfStream){
  7.     var str = f.ReadLine();
  8.     a[str]?a[str]++:a[str]=1;
  9. }
  10. f.Close();
  11. var c={}, e=[];
  12. for(var b in a){
  13.     if(!c[a[b]]){
  14.         c[a[b]]=[];
  15.         e.push(a[b]);
  16.     }
  17.     c[a[b]].push(b);
  18. }
  19. e.sort(function(a,b){return b-a});
  20. var f = fso.CreateTextFile('New_'+file, 2);
  21. for(var i=0;i<e.length;i++){
  22.     for(var j=0;j<c[e[i]].length;j++){
  23.         for(var k=0;k<e[i];k++)f.WriteLine(c[e[i]][j]);
  24.     }
  25. }
  26. WSH.echo('Done');
复制代码

作者: pcl_test    时间: 2016-9-7 00:05

第三方http://www.bathome.net/s/tool/index.html?key=gawk
  1. #*&cls&gawk -f "%~f0"&pause&exit
  2. BEGIN{
  3.     file="文本.txt";
  4.     while(getline<file>0)a[$0]++;
  5.     for(b in a){c[a[b]]=c[a[b]]"\n"b;if(!d[a[b]])d[a[b]]=a[b]}
  6.     for(i=asort(d);i>0;i--){
  7.         split(substr(c[d[i]], 2), list, "\n");
  8.         for(j=1;j<=length(list);j++){
  9.             for(k=1;k<=d[i];k++)print list[j]>"New_"file;
  10.         }
  11.         delete list;
  12.     }
  13.     print "Done";
  14. }
复制代码





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