获取零的长度(被一个打断)

Get lengths of zeroes (interrupted by ones)

我有一长列 1 和 0:

0
0
0
1
0
0
0
0
0
1
0
0
0
0
0
1
0
0
1
....

我可以很容易地得到 1 之间的平均零数(只是 total/ones):

ones=$(grep -c 1 file.txt)
lines=$(wc -l < file.txt)
echo "$lines / $ones" | bc -l

但是我怎样才能得到一个之间的零字符串的长度呢?在上面的简短示例中,它将是:

3
5
5
2

编辑:修复了最后一行是 0

的情况

awk 简单:

awk '/1/{print NR-prev-1; prev=NR;}END{if (NR>prev)print NR-prev;}'

在 bash 中并没有那么难,或者:

i=0
for x in $(<file.txt); do
  if ((x)); then echo $i; i=0; else ((++i)); fi
done
((i)) && echo $i 

您可以使用 awk:

awk '=="0"{s++} =="1"{if(s)print s;s=0} END{if(s)print(s)}'

解释:

特殊变量</code>包含一行文本的第一个<em>字段</em>(<em>列</em>)的值。除非您使用 <code>-F 命令行选项指定字段分隔符,否则它默认为宽空格 - 这意味着 </code> 将在您的示例中包含 <code>01

如果 </code> 的值等于 <code>0,则名为 s 的变量将递增,但如果 </code> 等于 <code>1 的当前值s 被打印(如果大于零)并重新初始化为 0。 (注意 awk 在第一次增量操作之前用 0 初始化 s

END 块在处理完最后一行输入后执行。如果文件以 0(s) 结尾,将打印文件末尾和最后一个 1 之间的 0 秒数。 (如果没有 END 块,它们将不会打印)

输出:

3
5
5
2

如果可以使用perl:

perl -lne 'BEGIN{$counter=0;} if ($_ == 1){ print $counter; $counter=0; next} $counter++' file
3
5
5
2

使用 awk 相同的逻辑实际上看起来更好:

awk '{print c; c=0} !{c++}' file 
3
5
5
2

如果您的 file.txt 只是 一列 1 和 0,您可以使用 awk 并将记录分隔符更改为“1\n”。这使得每个 "record" 都是“0\n”的序列,并且记录中 0 的计数是记录的长度除以 2。前导和尾随 1 和 0 的计数将是正确的。

awk 'BEGIN {RS="1\n"} { print length/2 }' file.txt

使用 awk,我会使用值为 0 的字段计算为 False 的事实:

awk '!{s++; next} {if (s) print s; s=0} END {if (s) print s}' file

这个returns:

3
5
5
2

另外,请注意 END 块打印出现在最后一个 1.

之后的任何 "remaining" 零

说明

  • !{s++; next} 如果字段不为真,即如果字段为 0,则增加计数器。然后,跳到下一行。
  • {if (s) print s; s=0} 否则,打印计数器的值并重置它,但只要它包含一些值(以避免在文件以 1 开头时打印 0) .
  • END {if (s) print s} 在处理文件后打印计数器的剩余值,但如果之前没有打印过。

纯bash:

sum=0
while read n ; do
    if ((n)) ; then
        echo $sum
        sum=0
    else
        ((++sum))
    fi
done < file.txt
((sum)) && echo $sum # Don't forget to output the last number if the file ended in 0.

我的尝试。不是很漂亮但是.. :3

grep -n 1 test.txt | gawk '{y=-x; print y-1; x=}' FS=":"

输出:

3
5
5
2

另一种方式:

perl -lnE 'if(m/1/){say $.-1;$.=0}' < file

"reset" 1.

时的行计数器

打印

3
5
5
2

今天这个问题似乎很受欢迎。加入晚会,这是另一个简短的 gnu-awk 命令来完成这项工作:

awk -F '\n' -v RS='(1\n)+' 'NF{print NF-1}' file
3
5
5
2

工作原理:

-F '\n'           # set input field separator as \n (newline)
-v RS='(1\n)+'    # set input record separator as multipled of 1 followed by newline
NF                # execute the block if minimum one field is found
print NF-1        # print num of field -1 to get count of 0

为了更容易阅读,我会包括 uniq

uniq -c file.txt | awk '/ 0$/ {print }'

最简单的解决方案是将 sedawk 一起使用,如下所示:

sed -n '$bp;/0/{:r;N;/0$/{h;br}};/1/{x;bp};:p;/.\+/{s/\n//g;p}' input.txt \
  | awk '{print length}'

解释:

sed 命令分隔 0 并创建如下输出:

000
00000
00000
00

通过管道传输到 awk '{print length}' 您可以获得每个间隔的 0 计数:

输出:

3
5
5
2

一个有趣的,纯粹的 Bash:

while read -d 1 -a u || ((${#u[@]})); do
    echo "${#u[@]}"
done < file

这告诉 read 使用 1 作为分隔符,即,一旦遇到 1 就停止阅读; read 将 0 存储在数组 u 的字段中。那么我们只需要用${#u[@]}统计u中的字段个数。 || ((${#u[@]})) 在这里是为了防止您的文件不以 1.

结尾

更奇怪(且不完全正确)的方式:

perl -0x31 -laE 'say @F+0' <file

打印

3
5
5
2
0

  • 读取记录分隔符设置为字符1的文件-0x31
  • 自动拆分 -a(将记录拆分到数组 @F
  • 并打印 @F 中的元素数量,例如say @F+0 或者可以使用 say scalar @F

不幸的是,在最后的 1(作为记录分隔符)之后,它打印了一个空记录 - 因此打印了最后一个 0.

这是不正确的解决方案,仅作为替代好奇心显示。

扩展,你可以说:

$ uniq -c file | awk '! {print }'
3
5
5
2

man uniq我们看到uniq的目的是:

Filter adjacent matching lines from INPUT (or standard input), writing to OUTPUT (or standard output).

因此 uniq 将数字分组。使用 -c 选项,我们得到一个带有出现次数的前缀:

$ uniq -c file
      3 0
      1 1
      5 0
      1 1
      5 0
      1 1
      2 0
      1 1

然后是打印 0 之前的那些计数器的问题。为此,我们可以使用 awk,例如:awk '! {print }'。即:如果字段是0.

则打印第二个字段