又一道sed题的多种解决

时间:2022-12-18 13:30:02

群里的讨论呵,将自个的答案发到blog上来

:(要求用sed)
     有一根长300厘米的棍子,从左往右涂5厘米黑色,空5厘米,然后再涂5厘米黑色,再空出5厘米,……从右往左涂4厘米黑色,空出4厘米,再涂4厘米黑色,再空出4厘米……两边依次涂完之后,问空白处有多少厘米
解答:
看到题目时,出题的人已经给了了一个思路了呵,就是先顺序(按五间隔)=>转置=>再顺序(按四间隔),要求用sed处理
然后我根据这个思路,写了一个简单的可行解:
  perl -e 'print "0"x300,"/n"'|sed 's/0000000000/1111100000/g;'|sed '//n/!G;s//(./)/(.*/n/)/&/2/1/;//D;s/.//'|sed -r 's/....(....)/1111/1/g' |sed '//n/!G;s//(./)/(.*/n/)/&/2/1/;//D;s/.//'
111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111
并且结合之前的一个sed脚本,有sed计算出了0个数
perl -e 'print "0"x300,"/n"'|sed 's/0000000000/1111100000/g;'|sed '//n/!G;s//(./)/(.*/n/)/&/2/1/;//D;s/.//'|sed -r 's/....(....)/1111/1/g' |sed '//n/!G;s//(./)/(.*/n/)/&/2/1/;//D;s/.//'|sed 's/1//g' |sed -r 'x; s/.*/0/; G; x; :a; /./! bout; s/.//; H; g; s//n.*//g; s/^9*$/0&/; s/.9*$/x&/; H; s/.*x//; y/0123456789/1234567890/; G; s/([^/n]*)/n.*/n([^/n]*)/n.*/n([^/n]*)x.*$//3/1/n/2/; x;s/.*/n([^/n]*)/n[^/n]*//1/; ba; :out; g;s//n/ /;p;'
然后群里的sun给出的另一个更简明的解:
seq 300|sed 's/.*//;n;s/.*//;n;s/.*//;n;s/.*//;n;n;n;n'|tac|sed 's/.*//;n;s/.*//;n;s/.*//;n;s/.*//;n;s/.*//;n;n;n;n;n'|grep -c .
seq 300|sed 's/.*//;n;s/.*//;n;s/.*//;n;s/.*//;n;n;n;n'|sed -n '1!G;$p;h'|sed 's/.*//;n;s/.*//;n;s/.*//;n;s/.*//;n;s
/.*//;n;n;n;n;n'|sed '/^$/d'|sed -n '$='
sun的思想无疑是清晰简单的呵,并且能用到较多的特性呵。
我发现自个在分析问题的时候,总是要给自己多设定条件了。使问题复杂化了
比如我那答案,事实上在考虑的时候,思维里就增加了一个条件:脚本应用于多行文件的,并分别得出每行的计算值了。
因此,我那答案在管道传输之羊,都是单行对单行的,
简单的说,在这种方法下各管道的sed,实际上可以直接合并成一个sed命令;
分开只是按思路上的分步进行处理

而sun的思路,不受这个限制的,很好的利用了管道间的行列转化呵。
经过进一步的思考,发现我之前那个sed上有bug呵,直接sed -r 's/....(....)/1111/1/g' 和 sed 's/0000000000/1111100000/g; 会出现临界点的错误呵。

于是想到的另一个解法:
perl -e 'print "0"x300;print "/n"'|sed -r 's/./a/g; s/(.{1,5})(.{0,5})//U/1/E/2/g;:a;//`/w/s//w{1,4}$//n/U&/m;//s//w{1,4}$//n&/m;ta; y/aA/n/01/x0/'
111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111

这个是目前能想到不用管道的方法了-_-(用管道就不能体现sed的图灵完备性了呵,这里假设输入已经给出)
当然,要想得到0的数值,加管道就简单多了,当然,用管道的话,再用sed就没多少意义了,随便用grep或wc啥的
可以
perl -e 'print "0"x300;'|sed -r 's/./a/g; s/(.{1,5})(.{0,5})//U/1/E/2/g;:a;//`/w/s//w{1,4}$//n/U&/m;//s//w{1,4}$//n&/m;ta; y/aA/n//n/x0/x0/ ' |sed -n '$='
74
[root@localhost ~]# perl -e 'print "0"x300;'|sed -r 's/./a/g; s/(.{1,5})(.{0,5})//U/1/E/2/g;:a;//`/w/s//w{1,4}$//n/U&/m;//s//w{1,4}$//n&/m;ta; y/aA/n/0/x0/x0/ ' |wc -c
74

其实 ,sed做为图灵完备(为什么现在老把sun这句话挂面嘴里了哈?),并不需要用多个命令(通道),单sed命令完全可以搞定
[root@localhost ~]# perl -e 'print "0"x300;print "/n"'|sed -nr 's/./a/g; s/(.{1,5})(.{0,5})//U/1/E/2/g;:a;//`/w/s//w{1,4}$//n/U&/m;//s//w{1,4}$//n&/m;ta; y/aA/n/0/x0/x0/; x; s/.*/0/; G; x; :b; /./! bout; s/.//; H; g; s//n.*//g; s/^9*$/0&/; s/.9*$/x&/; H; s/.*x//; y/0123456789/1234567890/; G; s/([^/n]*)/n.*/n([^/n]*)/n.*/n([^/n]*)x.*$//3/1/n/2/; x;s/.*/n([^/n]*)/n[^/n]*//1/; bb; :out; g;s//n/ /; s/ .*//;p'
74

前半部分相同,后半部分是之前写的一个sed行字符统计脚本,带原记录的,如果不想带原记录,可以更简化
写了这么多,其实还是那句话,shell脚本编程的思路决定效率,写这么复杂的sed脚本,除了具有学习和练习sed的应用,没有太多的实用价值。还是sun的思路灵活清晰
未了,再给出一个awk的解法和 perl 解决:

变量说明(可更改变量为任意正数试验)
n:初始字符个数,例子中为300
l:从左边开始间隔切换的个数,这边为5
r:从右边开始间隔切换的个数,这边为4
代码:
(输出第一行为结果,第二行为0字符数和1字符数


[root@localhost sed]# awk 'BEGIN{n=300;l=5;r=4;c=0;for(i=1;i<=n;i++){k=i%(2*l);j=(n-i+1)%(2*r);if((k>=1&& k<=l)||(j>=1 && j<=r)){printf "1"}else{printf "0";c++}};print "/n"c,n-c}'
111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111
74 226
[root@localhost sed]# awk 'BEGIN{n=100;l=5;r=4;c=0;for(i=1;i<=n;i++){k=i%(2*l);j=(n-i+1)%(2*r);if((k>=1&& k<=l)||(j>=1 && j<=r)){printf "1"}else{printf "0";c++}};print "/n"c,n-c}'
1111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111
24 76
perl解法:(强大的perl环视)
perl -e '$_="a"x300;  s/(.{1,5})(.{1,5})//U/1/E/2/g; s/a(?=.{0,3}(.{8})*$)/A/g; tr/aA$/01/;print $_,"/n"'
111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111111111110011111100001111100011111110111111111111001111110000111110001111111011111111111100111111000011111000111111101111