[新手上路]批处理新手入门导读[视频教程]批处理基础视频教程[视频教程]VBS基础视频教程[批处理精品]批处理版照片整理器
[批处理精品]纯批处理备份&还原驱动[批处理精品]CMD命令50条不能说的秘密[在线下载]第三方命令行工具[在线帮助]VBScript / JScript 在线参考
返回列表 发帖

[文本处理] 【已解决】求教:gawk 按字节拆分数据的运用

本帖最后由 思想之翼 于 2023-2-25 20:19 编辑
  1. "C:\SoftWare\gawk.exe" -F"=" "NR==FNR{A[$1]=$2;next}{print A[$0]}" "D:\00\a.txt" "D:\11\b.txt">"D:\22\c.txt"
复制代码
上列代码,将一组6位数按照1 3 5字节,拆分成1个文本。

现在有一组32位数,格式如下:
12345678123456781234567812345678
87654321876543218765432187654321

欲按每5个字节一组拆分,共拆分成201376个文本,结果如下:
000001.txt
12345
87654

000002.txt
12346
87653

000003.txt
12347
87652
...

201376.txt
45678
54321
1

评分人数

    • Batcher: 感谢给帖子标题标注[已解决]字样PB + 2

本帖最后由 WHY 于 2023-3-3 16:12 编辑

回复 18# 思想之翼


    这个问题得看电脑硬件配置了,我的电脑配置不咋地,多线程没想去玩。
每一行数据要循环处理201376次,然后又要写磁盘201376次,如果有10000行数据,别指望能快到哪去。

改一下,每读取2行数据开始写磁盘,减少循环等待时间及字典空间占用,我没有过多测试。你试试吧。
  1. Dim myPath, srcFile, dstFolder
  2. myPath  = Left(WSH.ScriptFullName, InStrRev(WSH.ScriptFullName, "\")) '脚本所在路径
  3. srcFile = myPath & "1.txt"                                            '源文本文件
  4. dstFolder = myPath & "result"                                         '目标目录
  5. Dim fso
  6. Set fso = CreateObject("Scripting.FileSystemObject")
  7. If Not fso.FolderExists(dstFolder) Then fso.CreateFolder(dstFolder)   '创建目标目录
  8. Dim c, objDic, objFile
  9. c = 5                                                                 'N选5组合
  10. Set objDic  = CreateObject("Scripting.Dictionary")                    '字典,存放结果
  11. Set objFile = fso.OpenTextFile(srcFile, 1)                            '打开源文件
  12. Dim num, strLine
  13. num = 0
  14. While Not objFile.AtEndOfStream
  15.     num = num + 1
  16.     strLine = objFile.ReadLine                                        '逐行读取源文件
  17.     GetCombination strLine, c, objDic                                 '求组合
  18.     If num Mod 2 = 0 Then                                             '每读取2行开始写入文件
  19.         SaveToFile objDic
  20.         objDic.RemoveAll                                              '清空字典
  21.     End If
  22. Wend
  23. If objDic.Count > 0 Then SaveToFile objDic                            '字典不为空,写入文件
  24. objFile.Close
  25. Function SaveToFile(ByRef oDict)
  26.     Dim key, dstFile, f
  27.     For Each key In oDict.Keys
  28.         dstFile = dstFolder & "\" & Right("000000" & key, 6) & ".txt" '目标文件名
  29.         Set f = fso.OpenTextFile(dstFile, 8, True)
  30.         f.Write(oDict.Item(key))                                     '写入目标文件
  31.         f.Close
  32.     Next
  33. End Function
  34. Function GetCombination(ByRef strLine, c, ByRef oDict)
  35.     Dim key, str, i
  36.     key = 1
  37.     str = ""
  38.     For i = 1 To Len(strLine)                  'str赋值:11111000000000000000000000000000 共32位
  39.         If i <= c Then
  40.             str = str & "1"
  41.         Else
  42.             str = str & "0"
  43.         End If
  44.     Next
  45.     If Not oDict.Exists(key) Then oDict.Add key, ""
  46.     oDict.Item(key) = oDict.Item(key) & Left(strLine, c) & vbCrLf     '字典,赋初始值
  47.     Dim reg
  48.     Set reg = New RegExp                                              '创建正则表达式
  49.     reg.Pattern = "10(?=(0*))\1(?=(1*))\2$"
  50.     While InStr(str, "10") > 0
  51.         Dim s
  52.         s = ""
  53.         str = reg.Replace(str, "01$2$1")                              'str值交换 10 <--> 01
  54.         For i = 1 To Len(str)
  55.             If Mid(str, i, 1) = "1" Then s = s & Mid(strLine, i, 1)   '查找"1"与strLine对应的字符
  56.             If Len(s) = c Then Exit For                               '找到5个,退出For
  57.         Next
  58.         key = key + 1
  59.         If Not oDict.Exists(key) Then oDict.Add key, ""
  60.         oDict.Item(key) = oDict.Item(key) & s & vbCrLf                '字典,赋值
  61.     Wend
  62. End Function
  63. MsgBox "Done"
复制代码
1

评分人数

TOP

回复 17# WHY
感谢!
处理成千上万行数据,同样存在读入内存耗时漫长的状况。

TOP

回复 9# WHY
感谢!
经过测试,该 PowerShell 脚本,处理十几行的数据非常迅速,但处理成千上万行数据,读入内存的耗时让人崩溃。
欲提高 PowerShell 脚本的效率,研究过类似下列网页的文章,还是不得要领,恳望指点。
https://www.pstips.net/speeding-up-powershell-multithreading.html

TOP

Test.vbs
  1. Dim myPath, srcFile, dstFolder
  2. myPath  = Left(WSH.ScriptFullName, InStrRev(WSH.ScriptFullName, "\")) '脚本所在路径
  3. srcFile = myPath & "1.txt"                                            '源文本文件
  4. dstFolder = myPath & "result"                                         '目标目录
  5. Dim fso
  6. Set fso = CreateObject("Scripting.FileSystemObject")
  7. If Not fso.FolderExists(dstFolder) Then fso.CreateFolder(dstFolder)   '创建目标目录
  8. Dim c, objDic, objFile
  9. c = 5                                                                 'N选5组合
  10. Set objDic  = CreateObject("Scripting.Dictionary")                    '字典,存放结果
  11. Set objFile = fso.OpenTextFile(srcFile, 1)                            '打开源文本文件
  12. While Not objFile.AtEndOfStream
  13.     Dim strLine
  14.     strLine = objFile.ReadLine                                        '逐行读取源文件
  15.     GetCombination strLine                                            '调用函数
  16. Wend
  17. objFile.Close
  18. Dim dstFile, f
  19. For Each key In objDic.Keys
  20.     dstFile = dstFolder & "\" & Right("000000" & key, 6) & ".txt"     '目标文件名
  21.     Set f = fso.OpenTextFile(dstFile, 2, True)
  22.     f.Write(objDic.Item(key))                                         '写入目标文件
  23.     f.Close
  24. Next
  25. Function GetCombination(strLine)
  26.     Dim key, str, i
  27.     key = 1
  28.     str = ""
  29.     For i = 1 To Len(strLine)                  'str赋值:11111000000000000000000000000000 共32位
  30.         If i <= c Then
  31.             str = str & "1"
  32.         Else
  33.             str = str & "0"
  34.         End If
  35.     Next
  36.     If Not objDic.Exists(key) Then objDic.Add key, ""
  37.     objDic.Item(key) = objDic.Item(key) & Left(strLine, c) & vbCrLf   '字典,赋初始值
  38.     Dim reg
  39.     Set reg = New RegExp                                              '创建正则表达式
  40.     reg.Pattern = "(1)(0)(?=(0*))\3(?=(1*))\4$"
  41.     While InStr(str, "10") > 0
  42.         Dim s
  43.         s = ""
  44.         str = reg.Replace(str, "$2$1$4$3")                            'str值交换 10 <--> 01
  45.         For i = 1 To Len(str)
  46.             If Mid(str, i, 1) = "1" Then s = s & Mid(strLine, i, 1)   '查找"1"与strLine对应的字符
  47.             If Len(s) = c Then Exit For                               '找到5个,退出For
  48.         Next
  49.         key = key + 1
  50.         If Not objDic.Exists(key) Then objDic.Add key, ""
  51.         objDic.Item(key) = objDic.Item(key) & s & vbCrLf              '字典,赋值
  52.     Wend
  53. End Function
  54. MsgBox "Done"
复制代码
1

评分人数

TOP

回复 15# terse


    是的,磁盘IO性能好的话,速度可能会快一点。
20万个文件频繁读写,时间都浪费在写磁盘上了。
在我的电脑上,机械硬盘,PS脚本大概62秒,GAWK很慢,我没敢测试。

TOP

这类组合类问题 主要效率上不去啊

TOP

回复 5# 思想之翼


如果需要上传文件,可以用阿里云盘或百度网盘。

如果需要上传截图,可以找个图床,例如:
http://bbs.bathome.net/thread-60985-1-1.html
我帮忙写的代码不需要付钱。如果一定要给,请在微信群或QQ群发给大家吧。
【微信公众号、微信群、QQ群】http://bbs.bathome.net/thread-3473-1-1.html
【支持批处理之家,加入VIP会员!】http://bbs.bathome.net/thread-67716-1-1.html

TOP

回复 12# hfxiang


   非常感谢您的帮助!

TOP

回复 11# 思想之翼


    没能看明白题意,抱歉

TOP

本帖最后由 思想之翼 于 2023-2-25 17:02 编辑

回复 8# hfxiang

感谢!能够运行,但结果不对。32位数,每5个字节组成一组,有201376种组合。

TOP

本帖最后由 WHY 于 2023-2-27 14:47 编辑

我也来个 gawk
  1. @echo Off
  2. md Result 2>nul
  3. gawk "BEGIN{FS=\"\"}{n=0;for(i=1;i<=NF-4;i++)for(j=i+1;j<=NF-3;j++)for(k=j+1;k<=NF-2;k++)for(L=k+1;L<=NF-1;L++)for(m=L+1;m<=NF;m++){++n;a[n]=a[n]$i$j$k$L$m\"\n\"}}END{for(i=1;i<=length(a);i++){f=\"Result/\"substr(1000000+i,2)\".Log\";printf a[i]>f;close(f)}}" 1.txt
  4. pause
复制代码
发现加上close(),关闭文件句柄速度会快很多。
1

评分人数

TOP

本帖最后由 WHY 于 2023-3-3 15:59 编辑

PowerShell 脚本,保存为Test.ps1,右键使用PowerShell运行
运行之前把杀软的文件实时监控关掉,否则频繁的磁盘操作CPU会达到100%,也会拖慢脚本速度

2023/03/03修改,假定每一行数据长度都相同,先创建一个N选M组合的索引表,每行数据按表搜索。
  1. $MyPath    = $MyInvocation.MyCommand.Path -replace '\\[^\\]*$', '\'; #脚本自身路径
  2. $srcFile   = $MyPath + '1.txt';              #源文件
  3. $dstFolder = $MyPath + 'result';             #目标目录
  4. If (![IO.Directory]::Exists($dstFolder)){
  5.     $null = md $dstFolder;
  6. }
  7. Function Get-Combination($n, $m){
  8.     $key = 1;
  9.     $str = '1' * $m + '0' * ($n-$m);         #11111000000000000000000000000000 共32位
  10.     $m = [regex]::Matches($str, '1');
  11.     $Hash.Add($key, $m.Index);               #HashTable赋初始值
  12.     while ($str.IndexOf('10') -ge 0) {
  13.         $str = $str -replace '10(?>(0*))(?>(1*))$', '01$2$1'; #交换 10 <--> 01
  14.         $m = [regex]::Matches($str, '1');
  15.         $Hash.Add(++$key, $m.Index);         #HashTable赋值
  16.     }
  17. }
  18. Function Set-ContentToFile($oDict){
  19.     forEach($key In $oDict.Keys){
  20.         $dstFile = $dstFolder + '\' + (''+$key).PadLeft(6, '0') + '.txt';  #目标文件名
  21.         [IO.File]::AppendAllText($dstFile, $oDict[$key], [Text.Encoding]::Default); #写入文件
  22.     }
  23. }
  24. $Hash = New-Object System.Collections.HashTable;
  25. $n = 32; $m = 5;                             #32选5组合
  26. Get-Combination $n $m                        #创建索引表,存放到$Hash中
  27. $arr = [IO.File]::ReadAllLines($srcFile, [Text.Encoding]::Default);  #读源文本
  28. $dic = New-Object 'System.Collections.Generic.Dictionary[int, string]'; #字典,存放结果
  29. $num = 0;
  30. for($i=0; $i -lt $arr.Count; $i++){
  31.     $chr = [char[]]$arr[$i];                 #转成字符数组
  32.     forEach( $key In $Hash.Keys ){           #遍历索引表
  33.         $s = '';
  34.         for ($j = 0; $j -lt $m; $j++){
  35.             $index = $Hash[$key][$j];
  36.             $s += $chr[$index];
  37.         }
  38.         If (!$dic.ContainsKey($key)) { $dic.Add($key, ''); }
  39.         $dic[$key] += $s + "`r`n";           #字典赋值
  40.     }
  41.     If (++$num % 2 -eq 0){                   #每读取2行数据开始写入文件
  42.         Set-ContentToFile $dic;
  43.         $dic.Clear();                        #清空字典
  44.     }
  45. }
  46. If ($dic.Count -gt 0){                       #字典不为空,写入文件
  47.     Set-ContentToFile $dic;
  48. }
  49. echo 'DONE';
  50. [Console]::ReadLine();
复制代码
1

评分人数

TOP

回复 7# 思想之翼


估计是版本问题,把单个“%”改为双“%%”试试,具体如下:
  1. gawk -vFPAT="[0-9]" "{for(i=0;i++<NF;){a[NR,i]=$i}}END{for(i=4;++i<=NF;){for(j=i;j<=NF;j++){fn=sprintf(\"%%06d.txt\",++n);print a[1,i-4]a[1,i-3]a[1,i-2]a[1,i-1]a[1,j]\"\n\"a[2,i-4] a[2,i-3] a[2,i-2] a[2,i-1] a[2,j]\"\n\">fn;close(fn)}}}" a.txt
复制代码
1

评分人数

TOP

回复 6# hfxiang
  1. gawk -vFPAT="[0-9]" "{for(i=0;i++<NF;){a[NR,i]=$i}}END{for(i=4;++i<=NF;){for(j=i;j<=NF;j++){fn=sprintf(\"%06d.txt\",++n);print a[1,i-4]a[1,i-3]a[1,i-2]a[1,i-1]a[1,j]\"\n\"a[2,i-4] a[2,i-3] a[2,i-2] a[2,i-1] a[2,j]\"\n\">fn;close(fn)}}}" a.txt
复制代码
chaifen.bat 代码如上

TOP

返回列表