免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 5047 | 回复: 8

关于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
发表于 2010-08-14 12:54 |显示全部楼层
本帖最后由 ly5066113 于 2010-08-14 13:27 编辑

关于sed的计数,在我的心得帖(http://bbs3.chinaunix.net/viewthread.php?tid=1762006)里面补充了3个例子,可能很多人没有看到,在这里重发下,看懂了这3个例子,相信大家对sed的计数不在会有任何问题。



        例8:实现 awk '{print NR,$0}' 的功能
        来自info sed中的examples(略有改动)
  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 的功能
        来自info sed中的examples(略有改动)
  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 。。。

        例10:实现 awk '{sum+=$1}END{print sum}'
        http://sed.sourceforge.net/grabbag/scripts/add_decs.sed
  1. ly5066113@ubuntu:~$ seq 100 | awk '{sum+=$1}END{print sum}'
  2. 5050
  3. ly5066113@ubuntu:~$ seq 100 | sed -nf test.sed
  4. 5050
  5. ly5066113@ubuntu:~$ seq 1000 | awk '{sum+=$1}END{print sum}'
  6. 500500
  7. ly5066113@ubuntu:~$ seq 1000 | sed -nf test.sed
  8. 500500
  9. ly5066113@ubuntu:~$ cat test.sed
  10. #! /usr/bin/sed -f

  11. # This is an alternative approach to summing numbers,
  12. # which works a digit at a time and hence has unlimited
  13. # precision.  This time it is done with lookup tables,
  14. # and uses only 10 commands.

  15. G
  16. s/\n/-/
  17. s/$/-/
  18. s/$/;9aaaaaaaaa98aaaaaaaa87aaaaaaa76aaaaaa65aaaaa54aaaa43aaa32aa21a100/

  19. :loop
  20. /^--[^a]/!{
  21.         # Convert next digit from both terms into analog form
  22.         # and put the two groups next to each other
  23.         s/^\([0-9a]*\)\([0-9]\)-\([^-]*\)-\(.*;.*\2\(a*\)\2.*\)/\1-\3-\5\4/
  24.         s/^\([^-]*\)-\([0-9a]*\)\([0-9]\)-\(.*;.*\3\(a*\)\3.*\)/\1-\2-\5\4/

  25.         # Back to decimal, but keeping the carry in analog form
  26.         # \2 matches an `a' if there are at least ten a's, else nothing
  27.         #
  28.         #    1-------------                       3-      4----------------------
  29.         #                        2                                                5----
  30.         s/-\(aaaaaaaaa\(a\)\)\{0,1\}\(a*\)\([0-9]*;.*\([0-9]\)\3\5\)/-\2\5\4/
  31.         b loop
  32. }
  33. s/^--\([^;]*\);.*/\1/
  34. h
  35. $p
复制代码
这段代码很巧妙,先是将需要相加的2个数字替换成对应的字母a的个数,然后将字母a合并在一起,
        在替换回相加后的结果数字,如果超过10就保留一个a表示进位。
        例如:123 + 456
        那么就先将3和6替换成aaa和aaaaaa,然后合并aaaaaaaaa
        这样在利用lookup table将aaaaaaaaa替换成9,就完成了加法的操作
        如果 125 + 456
        5和6相加最后就会变为a1,字母a回会在计算2加5的时候一并计算
        这样也就实现了进位。

评分

参与人数 2可用积分 +4 收起 理由
expert1 + 2 强大,不好理解啊!
badb0y + 2 收藏,回家慢慢看

查看全部评分

论坛徽章:
0
发表于 2010-08-14 13:08 |显示全部楼层

论坛徽章:
0
发表于 2010-08-14 13:10 |显示全部楼层
看基本能看懂,关键是自己应用的时候想不到是这样,或者不能变通,真是思想有多远,就能走多远阿

论坛徽章:
0
发表于 2010-08-14 13:17 |显示全部楼层
没错~以后大家Oracle或SQL方面有需求~ 等我水平高了也写一写感想HOHO~

论坛徽章:
0
发表于 2010-08-16 10:37 |显示全部楼层
up

论坛徽章:
16
IT运维版块每日发帖之星
日期:2015-08-24 06:20:00综合交流区版块每日发帖之星
日期:2015-10-14 06:20:00IT运维版块每日发帖之星
日期:2015-10-25 06:20:00IT运维版块每日发帖之星
日期:2015-11-06 06:20:00IT运维版块每日发帖之星
日期:2015-12-10 06:20:00平安夜徽章
日期:2015-12-26 00:06:302016猴年福章徽章
日期:2016-02-18 15:30:34IT运维版块每日发帖之星
日期:2016-04-15 06:20:00IT运维版块每日发帖之星
日期:2016-05-21 06:20:00综合交流区版块每日发帖之星
日期:2016-08-16 06:20:002015七夕节徽章
日期:2015-08-21 11:06:17IT运维版块每日发帖之星
日期:2015-08-14 06:20:00
发表于 2010-08-16 15:22 |显示全部楼层
回复 3# bbgg1983


    你这个“思想有多远”很经典啊,现在感觉瓶颈就是“想不到”,O(∩_∩)O哈哈~,看别人写的又感觉不是那么复杂。

论坛徽章:
0
发表于 2010-08-16 15:35 |显示全部楼层
mark下

论坛徽章:
0
发表于 2010-10-03 18:19 |显示全部楼层
回复 2# shileiadmin


    顶啊! 虽然半天才理解!

论坛徽章:
0
发表于 2010-10-03 22:02 |显示全部楼层
还是看不懂
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

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

清除 Cookies - ChinaUnix - Archiver - WAP - TOP