Board logo

标题: [文本处理] 【已解决】求助批处理文本内多列数据如何分别删重? [打印本页]

作者: 思想之翼    时间: 2022-10-24 22:56     标题: 【已解决】求助批处理文本内多列数据如何分别删重?

本帖最后由 思想之翼 于 2022-10-25 10:44 编辑

文本内数据有多列,单独各列有重复数据,如何将单独各列的重复数据删除仅保留一个。
文本数据格式如下所示:
8        4        8        3        4        6        3        3
8        3        7        4        5        5        6        3       
9        3        8                                       
结果为:
8        4        8        3        4        6        3        3
9        3        7        4        5        5        6
作者: WHY    时间: 2022-10-25 03:22

本帖最后由 WHY 于 2022-10-26 19:18 编辑

Test.bat,ANSI编码,文本中数据间的分隔符为单个Tab字符
  1. @if(0)==(0) echo off
  2. type a.txt | cscript //nologo //e:jscript "%~f0" > b.txt
  3. pause & exit/b
  4. @end
  5. Array.prototype.contains = function(s){       //添加contains方法
  6.     for( var i = 0; i < this.length; i++ ){
  7.         if( s == this[i] ) return true;
  8.     }
  9.     return false;
  10. }
  11. var out = [], max = 0;
  12. while( !WSH.StdIn.AtEndOfStream ){
  13.     var arrLine = WSH.StdIn.ReadLine().split(/\t/);  //按单个Tab字符分割成数组
  14.     for( var i = 0; i < arrLine.length; i++ ){
  15.         if( out[i] instanceof Array == false ){      //out[i]不是数组类型
  16.             out[i] = [];                             //初始化
  17.         }else if( out[i].contains(arrLine[i]) ){
  18.             continue;                                //out[i]去重复
  19.         }
  20.         out[i].push(arrLine[i]);                     //数据放入二维数组
  21.     }
  22. }
  23. for( var i = 0; i < out.length; i++ ){
  24.     max = max < out[i].length ? out[i].length : max; //求最大行数
  25. }
  26. for( var i = 0; i < max; i++ ){               //行
  27.     var str = '';
  28.     for( var j = 0; j < out.length; j++ ){    //列
  29.         str += ( out[j][i] == undefined ? '' : out[j][i] ) + '\t';
  30.     }
  31.     WSH.Echo(str);
  32. }
复制代码
Test.ps1 右键使用 PowerShell 运行
  1. $srcFile = 'a.txt';                   #修改前的文件名
  2. $dstFile = 'b.txt';                   #修改后的文件名
  3. $out = [Collections.ArrayList]@();    #数组,存放行、列数据
  4. $ret = [Collections.ArrayList]@();    #数组,返回处理结果
  5. $max = 0;                             #最大行数
  6. forEach( $strLine In [IO.File]::ReadAllLines($srcFile) ){
  7.     $arrLine = $strLine.Split("`t");                        #按单个Tab字符分割成数组
  8.     for( $i=0; $i -lt $arrLine.Count; $i++ ){
  9.         if( $out[$i] -isNot [Collections.ArrayList] ){      #如果$out[$i]不是数组类型
  10.             $null = $out.Add( [Collections.ArrayList]@() ); #数组$out添加$out[$i]
  11.         } elseIf( $out[$i].IndexOf($arrLine[$i]) + 1 ){
  12.             continue;                                       #数组$out[$i]去重
  13.         }
  14.         $null = $out[$i].Add( $arrLine[$i] );               #数组$out[$i]存放行列数据
  15.     }
  16. }
  17. for( $i=0; $i -lt $out.Count; $i++ ){
  18.     if( $max -lt $out[$i].Count ){ $max = $out[$i].Count; } #计算最大行数
  19. }
  20. for( $i=0; $i -lt $max; $i++ ){            #遍历行
  21.     $str = '';
  22.     for( $j=0; $j -lt $out.Count; $j++ ){  #遍历列
  23.         $str += $out[$j][$i] + "`t";
  24.     }
  25.     $null = $ret.Add($str);                #存放结果
  26. }
  27. [IO.File]::WriteAllLines($dstFile, $ret);  #输出
复制代码
在我的Win10电脑上,测试2000行10列的文本,PowerShell比js快很多,PS=0.3秒,js=3.5秒
一直以为文本处理js比PS快,出乎意料。
测试生成文本:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. (for /L %%i in (1,1,2000) do (
  4.     set "s="
  5.     for /L %%j in (1,1,10) do (
  6.         set /a r = !Random! %% 2000
  7.         set "s=!s!!r! "
  8.     )
  9.     echo;!s:~0,-1!
  10. ))>a.txt
复制代码

作者: hfxiang    时间: 2022-10-25 10:29

本帖最后由 hfxiang 于 2022-10-25 13:10 编辑

把待处理文件保存为“a.txt”,编码格式为ANSI,用第3方工具gawk( http://bcn.bathome.net/tool/4.1.0/gawk.exe )可实现:
  1. type a.txt|gawk -vORS="\t" "{max=max>NF?max:NF;for(i=1;i<=max;i++)a[NR,i]=$i}END{for(i=1;i<=max;i++){for(j=1;j<=NR;j++)if(!a[j,i]||!x[a[j,i],i]++)print a[j,i];printf \"\n\"}}"|gawk -F"\t" -vORS="\t" "{max=max>NF?max:NF;for(i=1;i<=max;i++)a[NR,i]=$i}END{for(i=1;i<=max;i++){for(j=1;j<=NR;j++){print (a[j,i]==\"\")?\"\":a[j,i]}printf \"\n\"}}"
复制代码

作者: 思想之翼    时间: 2022-10-26 14:13

本帖最后由 思想之翼 于 2022-10-26 16:41 编辑

回复 2# WHY
感谢帮助!欲在标红处添加循环语句,出错。恳望指点!
type a.txt | cscript //nologo //e:jscript "%~f0" > b.txt


@echo off
For f = 1 to 441
    For z = 1 to 100
@if(0)==(0) echo off
type e:\数据1\" & Right("00" & f, 3) & "\" & Right("00" & z, 3) & ".txt | cscript //nologo //e:jscript "%~f0" > e:\数据2\ "& Right("00" & f, 3) & "\" & Right("00" & z, 3) &" .txt
作者: WHY    时间: 2022-10-26 19:27

回复 4# 思想之翼


    Test.ps1 右键使用 PowerShell 运行
  1. Function RemoveDuplicateData($file){
  2.     $out = [Collections.ArrayList]@();    #数组,存放行、列数据
  3.     $max = 0;                             #最大行数
  4.     forEach( $strLine In [IO.File]::ReadAllLines($file) ){
  5.         $arrLine = $strLine.Split("`t");                        #按单个Tab字符分割成数组
  6.         for( $i=0; $i -lt $arrLine.Count; $i++ ){
  7.             if( $out[$i] -isNot [Collections.ArrayList] ){      #如果$out[$i]不是数组类型
  8.                 $null = $out.Add( [Collections.ArrayList]@() ); #数组$out添加$out[$i]
  9.             } elseIf( $out[$i].IndexOf($arrLine[$i]) + 1 ){
  10.                 continue;                                       #数组$out[$i]去重
  11.             }
  12.             $null = $out[$i].Add( $arrLine[$i] );               #数组$out[$i]存放行列数据
  13.        }
  14.     }
  15.     for( $i=0; $i -lt $out.Count; $i++ ){
  16.         if( $max -lt $out[$i].Count ){ $max = $out[$i].Count; } #计算最大行数
  17.     }
  18.     for( $i=0; $i -lt $max; $i++ ){            #遍历行
  19.         $str = '';
  20.         for( $j=0; $j -lt $out.Count; $j++ ){  #遍历列
  21.            $str += $out[$j][$i] + "`t";
  22.         }
  23.         $null = $result.Add($str);             #存放结果
  24.     }
  25. }
  26. for( $i = 1; $i -le 441; $i++ ){
  27.     for( $j = 1; $j -le 100; $j++ ){
  28.         $srcFile = 'E:\数据1\' + ('' + $i).PadLeft(3, '0') + '\' + ('' + $j).PadLeft(3, '0') + '.txt';
  29.         $dstFile = 'E:\数据2\' + ('' + $i).PadLeft(3, '0') + '\' + ('' + $j).PadLeft(3, '0') + '.txt';
  30.         if( ![IO.File]::Exists($srcFile) ) { continue; }
  31.         $result = [Collections.ArrayList]@();
  32.         RemoveDuplicateData $srcFile;
  33.         [IO.File]::WriteAllLines($dstFile, $result);  #写文件
  34.      }
  35. }
  36. echo 'Done';
  37. [Console]::ReadLine();
复制代码

作者: WHY    时间: 2022-10-26 19:30

Test.js
  1. var fso = new ActiveXObject('Scripting.FileSystemObject');
  2. Array.prototype.contains = function(s){       //添加contains方法
  3.     for( var i = 0; i < this.length; i++ ){
  4.         if( s == this[i] ) return true;
  5.     }
  6.     return false;
  7. }
  8. for( var i = 1; i <= 441; i++ ){
  9.     for( var j = 1; j <= 100; j++ ){
  10.         var srcFile = 'e:\\数据1\\' + ('00' + i).slice(-3) + '\\' + ('00' + j).slice(-3) + '.txt';
  11.         var dstFile = 'e:\\数据2\\' + ('00' + i).slice(-3) + '\\' + ('00' + j).slice(-3) + '.txt';
  12.         if( !fso.FileExists(srcFile) ) continue;
  13.         var result = [];
  14.         removeDuplicateData(srcFile);
  15.         fso.OpenTextFile(dstFile, 2, true).Write( result.join('\r\n') );
  16.     }
  17. }
  18. function removeDuplicateData(file){
  19.     var out = [], max = 0;
  20.     var objFile = fso.OpenTextFile(file, 1);
  21.    
  22.     while( !objFile.AtEndOfStream ){
  23.         var arrLine = objFile.ReadLine().split('\t');    //按单个Tab字符分割成数组
  24.         for( var i = 0; i < arrLine.length; i++ ){
  25.             if( out[i] instanceof Array == false ){      //out[i]不是数组类型
  26.                 out[i] = [];                             //初始化
  27.             }else if( out[i].contains(arrLine[i]) ){
  28.                 continue;                                //out[i]去重复
  29.             }
  30.             out[i].push(arrLine[i]);                     //数据放入二维数组
  31.         }
  32.     }
  33.     for( var i = 0; i < out.length; i++ ){
  34.         max = max < out[i].length ? out[i].length : max; //求最大行数
  35.     }
  36.     for( var i = 0; i < max; i++ ){               //行
  37.         var str = '';
  38.         for( var j = 0; j < out.length; j++ ){    //列
  39.             str += ( out[j][i] == undefined ? '' : out[j][i] ) + '\t';
  40.         }
  41.         result.push(str);
  42.     }
  43. }
  44. WSH.Echo('Done');
复制代码

作者: WHY    时间: 2022-10-29 14:04

想来想去总觉得2#哪里不对劲,今天终于发现了问题所在:添加的contains方法效率极其低下,每个数据都要调用一次contains,每次调用都要for循环遍历,总循环次数成几何级增加。

js改用散列数组代替自定义的contains方法:
  1. var t = (new Date()).getTime();
  2. var out = [], res = [], max = 0;
  3. var map = [];
  4. var fso = new ActiveXObject('Scripting.FileSystemObject');
  5. var objFile = fso.OpenTextFile('a.txt', 1);
  6. while( !objFile.AtEndOfStream ){
  7.     var arrLine = objFile.ReadLine().split('\t');    //按单个Tab字符分割成数组
  8.     var len = arrLine.length;
  9.     for( var i = 0; i < len; i++ ){
  10.         if( out[i] instanceof Array == false ){      //out[i]不是数组类型
  11.             out[i] = [];                             //初始化
  12.             map[i] = [];
  13.         }else if( map[i][arrLine[i]] ){
  14.             continue;                                //out[i]去重复
  15.         }
  16.         map[i][arrLine[i]] = true;
  17.         out[i].push(arrLine[i]);                     //数据放入二维数组
  18.     }
  19. }
  20. for( var i = 0; i < out.length; i++ ){
  21.     max = max < out[i].length ? out[i].length : max; //求最大行数
  22. }
  23. for( var i = 0; i < max; i++ ){               //行
  24.     var str = '';
  25.     for( var j = 0; j < out.length; j++ ){    //列
  26.         str += ( out[j][i] == undefined ? '' : out[j][i] ) + '\t';
  27.     }
  28.     res.push(str);
  29. }
  30. fso.OpenTextFile('b.txt', 2, true).Write( res.join('\r\n') );
  31. WSH.Echo((new Date()).getTime() - t);
复制代码
PowerShell改用HashTable containsKey方法代替数组IndexOf方法:
  1. $t = get-Date;
  2. $srcFile = 'a.txt';                   #修改前的文件名
  3. $dstFile = 'b.txt';                   #修改后的文件名
  4. $out = [Collections.ArrayList]@();    #数组,存放行、列数据
  5. $ret = [Collections.ArrayList]@();    #数组,返回处理结果
  6. $map = [Collections.ArrayList]@();    #数组,存放HashTable
  7. $max = 0;                             #最大行数
  8. forEach( $strLine In [IO.File]::ReadAllLines($srcFile) ){
  9.     $arrLine = $strLine.Split("`t");                        #按单个Tab字符分割成数组
  10.     for( $i=0; $i -lt $arrLine.Count; $i++ ){
  11.         if( $out[$i] -isNot [Collections.ArrayList] ){      #如果$out[$i]不是数组类型
  12.             $null = $out.Add( [Collections.ArrayList]@() ); #数组$out添加$out[$i]
  13.             $null = $map.Add( @{} );                        #数组$map添加$map[$i]
  14.         } elseIf( $map[$i].ContainsKey($arrLine[$i]) ){
  15.             continue;                                       #数组$out[$i]去重
  16.         }
  17.         $null = $out[$i].Add( $arrLine[$i] );               #数组$out[$i]存放列数据
  18.         $map[$i].Add( $arrLine[$i], $true );                #添加到HashTable
  19.     }
  20. }
  21. for( $i=0; $i -lt $out.Count; $i++ ){
  22.     if( $max -lt $out[$i].Count ){ $max = $out[$i].Count; } #计算最大行数
  23. }
  24. for( $i=0; $i -lt $max; $i++ ){            #遍历行
  25.     $str = '';
  26.     for( $j=0; $j -lt $out.Count; $j++ ){  #遍历列
  27.         $str += $out[$j][$i] + "`t";
  28.     }
  29.     $null = $ret.Add($str);                #存放结果
  30. }
  31. [IO.File]::WriteAllLines($dstFile, $ret);  #输出
  32. ((get-Date) - $t).TotalSeconds
  33. [Console]::ReadLine();
复制代码
测试10000行10列的文本,两个脚本用时都在 0.5s 以内,基本算是正常了。
生成测试文本:
  1. @echo off
  2. setlocal enabledelayedexpansion
  3. (for /L %%i in (1,1,10000) do (
  4.     set "s="
  5.     for /L %%j in (1,1,10) do (
  6.         set /a r = !Random! %% 8000
  7.         set "s=!s!!r! "
  8.     )
  9.     echo;!s:~0,-1!
  10. )) > a.txt
复制代码





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