![Board logo](images/default/logo.gif)
标题: [文本处理] [已解决]如何使用文本工具进行整行去重、首列排序 [打印本页]
作者: 77七 时间: 2023-6-3 16:45 标题: [已解决]如何使用文本工具进行整行去重、首列排序
本帖最后由 77七 于 2023-6-5 13:25 编辑
请教各位老师、大佬:
如何使用gawk或sed或grep等专业文本处理工具 对文本进行以下处理
1.按整行进行去重(如 第一行、第三行相同,则去除第三行);
2.仅对第一列进行排序(第一列相同的所有行保持原有的上下顺序)
文本 编码ansi 数万行 含有多列(空格分隔) 含有数字、汉字、符号,不含引号、叹号;
批处理 编码ansi
如 1.txt- 江苏 盐城
- 江苏 苏州 宿迁
- 浙江 杭州
- 江苏 南京
- 江苏 盐城
- 江苏 苏州
- 安徽 合肥
复制代码
20230605 13:25 重新修改 1.txt
- 浙江 杭州
- 江苏 盐城
- 江苏 苏州 宿迁
- 江苏 南京
- 江苏 盐城
- 江苏 苏州
- 安徽 合肥
复制代码
希望几秒内处理完成,结果直接覆盖,或者得到 2.txt
2.txt
20230605 13:19 修改
utf-8编码的1.txt sort排序及手动处理结果:
- 江苏 盐城
- 江苏 苏州 宿迁
- 江苏 南京
- 江苏 苏州
- 浙江 杭州
- 安徽 合肥
复制代码
ansi编码的1.txt sort排序及手动处理结果:
- 安徽 合肥
- 江苏 盐城
- 江苏 苏州 宿迁
- 江苏 南京
- 江苏 苏州
- 浙江 杭州
复制代码
如上,排序了江苏、浙江、安徽;去除了重复行(第二个 江苏 盐城);
江苏 盐城;江苏 苏州 宿迁;江苏 南京;江苏 苏州; 的上下顺序不变。
谢谢!
补充说明:
上述2.txt结果是我错误的对utf-8编码的1.txt经过sort排序后,手动加工形成的。
像江苏、浙江、安徽的上下顺序,不影响实际使用。
作者: buyiyang 时间: 2023-6-3 21:11
本帖最后由 buyiyang 于 2023-6-3 21:14 编辑
- gawk "{a[$1][FNR]=$0;if (!($1 in order)) {order[length(order) + 1] = $1}} END {for (i = 1; i <= length(order); i++) {n = asorti(a[order[i]], b);for (j = 1; j <= n;j++) {if (!(a[order[i]][b[j]] in s)) {print a[order[i]][b[j]];s[a[order[i]][b[j]]] = 1}}}}" 1.txt > 2.txt
复制代码
作者: 77七 时间: 2023-6-3 21:26
回复 2# buyiyang
谢谢大佬,测试正确,非常感谢!
作者: qixiaobin0715 时间: 2023-6-4 09:48
试了试,纯P也能处理,确实要慢许多。
作者: terse 时间: 2023-6-4 10:21
- gawk "{if(!c[$0]++){if(!f[$1]++)b[++n]=$1;a[$1][++m[$1]]=$0}}END{for(i=1;i<=n;i++)for(j=1;j<=m[b[i]];j++)print(a[b[i]][j])}" 1.txt >2.txt
复制代码
作者: 77七 时间: 2023-6-4 11:26
回复 4# qixiaobin0715
感谢大佬关注!因为文本是不断变化的,又是几万行,经常需要处理,就没有考虑纯批处理。现在试了一下,我发现了一个知识点,批处理的sort命令 对 1楼的文本保存为ansi 和utf-8 的排序结果居然是不一样的。
作者: 77七 时间: 2023-6-4 11:28
回复 5# terse
感谢大佬帮助!测试结果正确。
作者: qixiaobin0715 时间: 2023-6-5 09:35
本帖最后由 qixiaobin0715 于 2023-6-5 09:36 编辑
回复 6# 77七
想到第三方也基本上隐性的使用临时文件,如果用纯P的话,在特定位置使用临时文件是不是效率要高一些,因为没有测试文本测试,结果不知如何:- @echo off
- (for /f "tokens=1*" %%i in (1.txt) do (
- if not defined _"%%i" (
- set _"%%i"=true
- findstr /lb "%%i " 1.txt>temp
- for /f "delims=" %%a in (temp) do (
- if not defined _"%%a" (
- set _"%%a"=true
- echo,%%a
- )
- )
- )
- ))>2.txt
- del temp
- pause
复制代码
作者: 77七 时间: 2023-6-5 11:27
本帖最后由 77七 于 2023-6-5 12:17 编辑
回复 8# qixiaobin0715
谢谢大佬,使用 http://www.bathome.net/redirect.php?goto=findpost&;ptid=12081&pid=76757 测试代码用时53分钟。这个耗时代码可能有点问题,结果少了一个0。文本有5万3千行。
作者: 77七 时间: 2023-6-5 12:03
本帖最后由 77七 于 2023-6-5 17:12 编辑
回复 2# buyiyang
大佬,能不能帮我再修改一下,实际使用中,我发现脚本没有对首列进行排序,仅仅是将首列相同的行排在了一起。
1楼的2.txt的结果,是我错误使用批处理的sort命令,对utf-8编码的1.txt排序了江苏、浙江、安徽后,手动调整第二列及以后列 顺序后形成的,和1.txt中的江苏、浙江、安徽行首次出现的上下顺序相同,属于巧合。如果sort ansi编码的1.txt文本,安徽是排在第一个的。
可能让您造成了一定程度的误解,不好意思。
我希望它们有某一种顺序就行,就像批处理中的 sort 排序utf-8 或ansi ,正逆序也都可以。
可能用数字描述更准确,实际使用中首列数字、汉字、字母都有。以下仅演示排序。
- 处理前
- 2 3
- 1 5
- 2 1
- 1 8
- 3 0
-
- 处理后
- 1 5
- 1 8
- 2 3
- 2 1
- 3 0
复制代码
谢谢大佬,terse大佬已经帮助我解决了问题。
作者: aloha20200628 时间: 2023-6-5 12:27
用变量是否已被定义来判断目标数据‘有无’,的确是纯P的一款利器,但受限于cmd总内存卡在64MB,有时可能也会打爆cmd,例如用其对应数万+行数据时... 参见》http://www.bathome.net/thread-29022-1-1.html
本帖的另一趣点是文本行去重。看过国内外有关网站的很多老帖,用纯P解决文本行去重的经典招数,还是那个 awk 句式,简洁漂亮高效!
awk.exe "!existLines[$0]++" inF.txt>outF.txt
虽然其算法用纯P或vbs或js均可实现,但动态编码与静态编码之比的运行效率显然是立见高下...
作者: 77七 时间: 2023-6-5 12:47
回复 11# aloha20200628
谢谢老师指点!gawk去重确实很好用,但是我主要是想解决首列排序的问题。
作者: 77七 时间: 2023-6-5 14:12
如果使用gawk去重,使用批处理排序首列,我的思路是这样的,将行号添加到第二列前面,然后sort “第一列行号第二列”,能排序首列,并且不改变首列相同行的上下顺序。只是一直 set 效率非常低。
- @echo off
- gawk "!existLines[$0]++" 1.txt>2.txt
- setlocal enabledelayedexpansion
- for /f "tokens=1-2* delims=: " %%a in ('type "2.txt" ^|findstr /n .*') do (
- echo %%a
- set n=00000000%%a
- set "#%%b#!n:~-7!=%%c"
- )
- (for /f "tokens=1-2* delims=#=" %%a in ('set # ^|sort') do (
- echo %%a %%c
- ))>3.txt
- endlocal
- pause
复制代码
作者: qixiaobin0715 时间: 2023-6-5 14:22
回复 9# 77七
纯P看来是不行。建议发一个测试的文本,以供对此类问题感兴趣的同仁练习用。
作者: 77七 时间: 2023-6-5 14:43
回复 14# qixiaobin0715
实际文本也很简单,就两三列简短的数据,使用随机数可以创建一个类似的。- @echo off
- setlocal enabledelayedexpansion
- (for /l %%l in (1,1,50000) do (
- set n=!random!
- set m=!random!
- echo !n! !m!
- ))>1.txt
复制代码
作者: terse 时间: 2023-6-5 14:55
楼主是想让安徽排在第一个这样的排序吗
作者: 77七 时间: 2023-6-5 15:00
本帖最后由 77七 于 2023-6-5 15:12 编辑
回复 16# terse
大佬,像以下这样的,数字比较好说明,汉字因为编码问题,排序有差异又出现巧合。实际文本是ansi,只要排序了就行,比如以下数字,可以排成 1 2 3 ,也可以 3 2 1 。
- 处理前
- 2 3
- 1 5
- 2 1
- 1 8
- 3 0
-
- 处理后
- 1 5
- 1 8
- 2 3
- 2 1
- 3 0
复制代码
作者: xczxczxcz 时间: 2023-6-5 15:38
吐个槽:学一门对象语言,不就简单了吗,用你上面的50000行脚本,用PS排序分类也秒内完成。唉,为何总纠结bat?
作者: terse 时间: 2023-6-5 16:17
去重复 然后排序,只是不确定你想怎样排序,是一次性排序,还是只排关键词,试试吧,看达不到要求- gawk "{if(!c[$0]++)a[$1][b[$1]++]=$0}END{PROCINFO[\"sorted_in\"]=\"@ind_str_asc\";for(k in a)for(i=0;i<b[k];i++) print a[k][i]}" 1.txt>2.txt
复制代码
回复 17# 77七
作者: 77七 时间: 2023-6-5 16:24
回复 18# xczxczxcz
谢谢大佬指点!注册账号以前没接触过计算机语言,包括现在我也不知道什么是对象语言...因为现在bat基本可以满足我的实际使用需要,还有很多第三方工具可以调用补充bat的不足,所以我的想法是把bat学好就行,自己实用为主,暂时不考虑学习ps。
作者: 77七 时间: 2023-6-5 17:10
回复 19# terse
谢谢大佬,测试没问题,完全满足需求。批处理中的 sort 会按整行排序,排序第一列同时会排序第二列。我是想对第二列不排序,保持原有顺序。
大概是1楼,由于我的错误,和巧合,让您造成了误解,不好意思。描述起来可能比较困难,本意像13楼中我的批处理排序代码那样,第一列相同的,行号小的第二列会排在上面。
作者: terse 时间: 2023-6-5 19:32
一定执着于批处理的话 这样处理怎么样,效率提高不大,比较SET还是否轻松点吧- @echo off&setlocal enabledelayedexpansion
- REM 存为ANSI文件
- %1 (for /f "tokens=1,2*" %%i in ('%0 rem ^|sort') do echo %%i %%k)>2.txt&exit
- for /f "tokens=1,2*delims=: " %%a in ('findstr /n . ^< a.txt') do (
- if not defined _"%%b %%c" (
- set s=000000%%a
- echo %%b !s:~-7! %%c
- )
- set _"%%b %%c"=0
- )
复制代码
作者: 77七 时间: 2023-6-5 20:34
回复 22# terse
谢谢大佬!没有执着用批处理解决这个问题。这种写法学习了!测试了一下,用时12分钟。
我把代码中的去重去掉了,(因为还在set,在下午测试的时候我发现批处理set 到三四万个变量后,速度已经变得很慢 )。直接使用gawk去重,使用代码排序,耗时只需1分钟,效率已大大提高!
作者: WHY 时间: 2023-6-5 21:41
本帖最后由 WHY 于 2023-6-6 09:38 编辑
bat+JS 试试- @if(0)==(0) echo off
- pushd "%~dp0"
- type 1.txt | cscript //nologo //e:jscript "%~f0" > 2.txt
- pause & exit/b
- @end
-
- var map = {};
- var num = 100000000;
-
- var str = WSH.StdIn.ReadAll();
- var arr = str.replace(
- /^(\S+)[^\r\n]*(?=\r\n|$)/mg,
- function(s0, s1){
- if (!map.hasOwnProperty(s0)){
- map[s0] = true;
- return s1 + ++num + ' ' + s0; //每一行添加数字
- } else {
- return ''; //去重复
- }
- }
- ).split('\r\n').sort();
-
- str = arr.join('\r\n').replace(/^\S+ /mg, ''); //删掉每一行添加的数字
- str = str.replace(/^(?:\r\n)+/, ''); //删掉开头的回车换行
- WSH.Echo(str);
复制代码
作者: 77七 时间: 2023-6-5 21:58
回复 24# WHY
谢谢大佬!速度也很快,5万行文本,1-2秒左右。
作者: aloha20200628 时间: 2023-6-9 12:33
既然LZ的主旨是要用纯P组织流程与控制,用高效第三方工具作任务执行,就用awk+sort的组合拳了。
从本站下载 sort.exe v2.1/v7.6 两个版皆可,最好更名调用(如_sort.exe)以免和系统内置款冲突。
例如》 _sort -k 1 -t ";" inF.txt>outF.txt
上式中的参数-k指定仅对第1列排序,参数-t指定各列分隔符为分号,默认是空格。
作者: 77七 时间: 2023-6-9 14:42
回复 26# aloha20200628
谢谢老师指点!我试了一下
复制代码
没有达到效果,仍然排序了第二列。
- 排序前
- 2 7
- 1 5
- 2 1
- 1 2
- 1 3
- 排序后
- 1 2
- 1 3
- 1 5
- 2 1
- 2 7
-
- 希望排序后
- 1 5
- 1 2
- 1 3
- 2 7
- 2 1
复制代码
看了sort -k的说明,我也没看明白。
-k, --key=POS1[,POS2] 在 POS1(起点 1)开始一个键,在 POS2 结束
(默认行尾)
作者: hfxiang 时间: 2023-6-9 18:03
回复 17# 77七 - gawk "!a[$1]++{c[++i]=$1}{b[$1,a[$1]]=$0}END{s=asort(c,tA);for(i=1;i<=s;i++)for(j=1;j<=a[tA[i]];j++)print b[tA[i],j]}" 1.txt>2.txt
复制代码
作者: 77七 时间: 2023-6-9 20:37
回复 28# hfxiang
谢谢大佬!大佬的代码对首列的排序方式有点与众不同,测试排序数字,大概是按数值大小排序的;测试排序汉字,大致和批处理的sort排序相同;满足排序需求,非常感谢!
作者: aloha20200628 时间: 2023-6-9 21:19
看来满足LZ的特殊要求还须一个参数 -s 帮忙,订正为下式即可 》
_sort -s -k 1,1 inF.txt>outF.txt
再分享一个有关 _sort.exe 的趣点,单用(必须是单用!)一个参数 -u 还可去重,如下式》
_sort -u inF.txt>outF.txt
如此,去重+排序 可被 _sort.exe 一举拿下...
作者: 77七 时间: 2023-6-9 22:00
回复 30# aloha20200628
谢谢老师指点!
测试排序,结果正确!
测试去重,默认带了排序功能。
如果只是简单的去重排序,代码简短容易记忆,堪称神器!
非常感谢!
欢迎光临 批处理之家 (http://bathome.net./) |
Powered by Discuz! 7.2 |