免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
楼主: ly5066113
打印 上一主题 下一主题

[学习共享] 文本编辑的一点心得--sed篇 [复制链接]

论坛徽章:
0
41 [报告]
发表于 2010-08-06 09:26 |只看该作者
sed命令在处理宽字节字符和长宽字节字符方面显得比较吃力。

可以这样说:这一点是sed的一块短板。类似sed命令处理宽字节字

符和长宽字节字符显得比较吃力的命令还有rev命令。

    如果遇到百万级别的处理记录。例如一份500W条记录的邮箱地

址要求剔除里面含有非法字符的邮箱地址,保留正确合法的邮箱地址,

并把被剔除的非法邮箱地址也统计出来。如果这个500W条记录的文

件里面不包含有宽字节字符或者长宽字节字符,则用sed和rev还算勉

强可以处理。如果这个500W条记录的文件里面包含有宽字节和长宽

字节字符的记录,用sed和rev一处理就会报错。处理结果也不够理想,

有些包含有宽字节字符和长宽字节字符的非法邮箱记录根本就没有被

筛选出来。

论坛徽章:
33
ChinaUnix元老
日期:2015-02-02 08:55:39CU十四周年纪念徽章
日期:2019-08-20 08:30:3720周年集字徽章-周	
日期:2020-10-28 14:13:3020周年集字徽章-20	
日期:2020-10-28 14:04:3019周年集字徽章-CU
日期:2019-09-08 23:26:2519周年集字徽章-19
日期:2019-08-27 13:31:262016科比退役纪念章
日期:2022-04-24 14:33:24
42 [报告]
发表于 2010-08-06 09:32 |只看该作者
前段时间要处理一个几千万行的文本,vi根本打不开,多亏了有sed,嘿嘿。

论坛徽章:
0
43 [报告]
发表于 2010-08-06 09:55 |只看该作者
好崇拜TIM!!!!!!

论坛徽章:
1
CU十二周年纪念徽章
日期:2013-10-24 15:41:34
44 [报告]
发表于 2010-08-06 10:20 |只看该作者
路过帮顶

论坛徽章:
0
45 [报告]
发表于 2010-08-06 11:41 |只看该作者
学习之

论坛徽章:
0
46 [报告]
发表于 2010-08-06 15:00 |只看该作者
正在努力把awk和sed搞熟悉中!!!

论坛徽章:
23
15-16赛季CBA联赛之吉林
日期:2017-12-21 16:39:27白羊座
日期:2014-10-27 11:14:37申猴
日期:2014-10-23 08:36:23金牛座
日期:2014-09-30 08:26:49午马
日期:2014-09-29 09:40:16射手座
日期:2014-11-25 08:56:112015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:49:0315-16赛季CBA联赛之山东
日期:2017-12-21 16:39:1915-16赛季CBA联赛之广东
日期:2016-01-19 13:33:372015亚冠之山东鲁能
日期:2015-10-13 09:39:062015亚冠之西悉尼流浪者
日期:2015-09-21 08:27:57
47 [报告]
发表于 2010-08-06 15:19 |只看该作者
感谢斑竹加精。


关于sed记数,补充2个例子,均来自info sed中的examples(略有改动)

        例8:实现 awk '{print NR,$0}' 的功能
  1. ly5066113@ubuntu:~$ head sed.info | awk '{print NR,$0}'
  2. 1 File: sed.info,  Node: Top,  Next: Introduction,  Up: (dir)
  3. 2
  4. 3 sed, a stream editor
  5. 4 ********************
  6. 5
  7. 6 This file documents version 4.1.5 of GNU `sed', a stream editor.
  8. 7
  9. 8    Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software
  10. 9 Foundation, Inc.
  11. 10
  12. ly5066113@ubuntu:~$ head sed.info | sed -nf test.sed
  13. 1 File: sed.info,  Node: Top,  Next: Introduction,  Up: (dir)
  14. 2
  15. 3 sed, a stream editor
  16. 4 ********************
  17. 5
  18. 6 This file documents version 4.1.5 of GNU `sed', a stream editor.
  19. 7
  20. 8    Copyright (C) 1998, 1999, 2001, 2002, 2003, 2004 Free Software
  21. 9 Foundation, Inc.
  22. 10
  23. ly5066113@ubuntu:~$ cat test.sed
  24. #! /usr/bin/sed -f
  25. x
  26. 1s/^/1/
  27. G
  28. s/\n/ /p
  29. s/ .*//
  30. /^9*$/s/^/0/
  31. s/.9*$/x&/
  32. h
  33. s/.*x//
  34. y/0123456789/1234567890/
  35. x
  36. s/x.*//
  37. G
  38. s/\n//
  39. h
复制代码
x                              #交换pattern space与hold space,保存读入的内容
        1s/^/1/                        #如果是第一行,初始化行号1
        G                              #将保存的内容追加回pattern space
        s/\n/ /p                       #将换行替换为空格,并打印
        s/ .*//                        #去处空格以后的所有内容,pattern space只剩下行号
        /^9*$/s/^/0/                   #如果行号都为9,在前面补0
        s/.9*$/x&/                     #用x分隔不需要改变和需要改变的数字
        h                              #将pattern space中的内容保存到hold space
        s/.*x//                        #删除不需要改变的数字
        y/0123456789/1234567890/       #对数字进行 +1 的操作
        x                              #交换pattern space与hold space
        s/x.*//                        #删除需要改变的数字
        G                              #将改变后的数字追加回pattern space
        s/\n//                         #删除换行,得到新的行号
        h                              #保存新行号到hold space

        整体的思路:
        每读入一行记录,将保存在hold space中的行号(如果是第一行,需要初始化),和本行记录合并输出
        然后将行号 +1 ,保存至hold space

        代码的核心部分就是实现“行号 +1”,具体做法:
        将行号分为2个部分,一部分保持不变(此部分可能没有),一部分进行改变,然后将需要改变的部分进行处理
        将处理后的结果与前面不变的部分拼接在一起,形成新的行号,但这里有个情况需要考虑,就是进位
        我们已实际的例子来看看
        如果行号是 123
        那么首先将其分成2个部分变为  12x3
        x左边的12不需要改变,x右边的3需要变成4
        那么就对3进行y/0123456789/1234567890/的操作,将其变成4
        然后拼上前面的12变为 124
        如果行号是129(需要进位)
        那么首先将其分成2个部分变为  1x29
        x左边的1不需要改变,x右边的29需要变成30
        那么就对29进行y/0123456789/1234567890/的操作,将其变成30
        然后拼上前面的1变为 130
        这里面有个特殊情况需要考虑,就是如果行号都为9
        9, 99, 999 之类的情况,那么我们需要在前面加数字0用做进位用

        例9:实现 wc -c 的功能
  1. ly5066113@ubuntu:~$ wc -c urfile
  2. 254 urfile
  3. ly5066113@ubuntu:~$ sed -nf test.sed urfile
  4. 254
  5. ly5066113@ubuntu:~$ cat test.sed
  6. #! /usr/bin/sed -f
  7. s/./a/g
  8. H
  9. x
  10. s/\n/a/
  11. : a;  s/aaaaaaaaaa/b/g; t b; b done
  12. : b;  s/bbbbbbbbbb/c/g; t c; b done
  13. : c;  s/cccccccccc/d/g; t d; b done
  14. : d;  s/dddddddddd/e/g; t e; b done
  15. : e;  s/eeeeeeeeee/f/g; t f; b done
  16. : f;  s/ffffffffff/g/g; t g; b done
  17. : g;  s/gggggggggg/h/g; t h; b done
  18. : h;  s/hhhhhhhhhh//g
  19. : done
  20. $! {
  21.         h
  22.         b
  23. }
  24. : loop
  25. /a/! s/[b-h]*/&0/
  26. s/aaaaaaaaa/9/
  27. s/aaaaaaaa/8/
  28. s/aaaaaaa/7/
  29. s/aaaaaa/6/
  30. s/aaaaa/5/
  31. s/aaaa/4/
  32. s/aaa/3/
  33. s/aa/2/
  34. s/a/1/
  35. y/bcdefgh/abcdefg/
  36. /[a-h]/ b loop
  37. p
复制代码
此段代码虽然看起来烦琐,但思路却比上一例好理解。
        每读一行数据,将里面所有的字符都替换成字母a,因为sed读数据时会将换行符(\n)去掉
        所以我们利用H命令产生的\n将其补充回来,也替换成字母a,统一做字符统计
        为了节省内存开销,提高效率,这里做了进位的处理,就是将10个a替换成1个b,10个b替换成1个c 。。。
        这样到最后,字母a的个数就代表个位数字,字母b的个数就代表十位数字,字母c的个数代表百位数字。。。
        如果最后剩下是这样一串字符:
        ccbbbbbaaaa
        那么就表示总共的字符数为:254
        本段代码的统计是有上限的,如果字符数量超过1亿,将无法得到正确结果
        可以通过增加替换的次数来增加统计上限,如 s/hhhhhhhhhh/i/g , s/iiiiiiiiii/j/g 。。。

论坛徽章:
0
48 [报告]
发表于 2010-08-06 15:27 |只看该作者
再学习

论坛徽章:
1
巨蟹座
日期:2014-06-04 13:33:30
49 [报告]
发表于 2010-08-06 15:41 |只看该作者
回复 47# ly5066113


    其实你应该开新帖,否则只有1/4的人看得到

论坛徽章:
2
巳蛇
日期:2014-10-26 22:38:12天蝎座
日期:2016-01-08 09:25:17
50 [报告]
发表于 2010-08-07 10:22 |只看该作者
好文,学习sed
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP