【问题标题】:How to count consecutive (repeated) character in string in bash?如何在bash中计算字符串中的连续(重复)字符?
【发布时间】:2018-02-09 07:17:18
【问题描述】:

我想知道是否有一个简单的 bash 或 AWK oneliner 来获取每次重复的重复字符数。

例如考虑这个字符串:

AATGATGGAANNNNNGATAGAACGATNNNNNNNNGATAATGANNNNNNNTAGACTGA

是否可以得到第一次重复的N个数,第二个重复的N个数等等?

谢谢!

预期结果,新行上每个重复的长度。

【问题讨论】:

  • 你做了哪些努力?发布它们,即使它没有解决您的问题
  • 至少添加您的预期输出 - 全部在一行,空格或逗号之间,在单独的行等...
  • 我对 anubhava 的第一个答案感到满意,请参阅他的答案下的 cmets。我按照您的要求添加了预期结果。
  • 我们不是在寻找对预期结果的描述(尽管也可以),我们正在寻找给定您发布的输入的实际预期输出。这个网站不仅可以让您获得问题的答案,它还是其他人查找问题以找到答案的存储库,因此问题必须是完整的(请参阅How to Ask)以帮助其他人未来。

标签: regex bash awk sed


【解决方案1】:

一个简单的解决方案:

echo "$string" | grep -oE "N+" | awk '{ print $0, length}'

NNNNN 5
NNNNNNNN 8
NNNNNNN 7

编辑:
根据@Ed-Morton 的建议:将 -P 更改为 -E。
grep 的手册页说 -P 是“高度实验性”的功能。
我们不需要 PCRE 来使用 +,只需 ERE 就足够了。

【讨论】:

  • 你不需要 PCRE 来使用 +,只需要 ERE,所以使用 -E 而不是 -P 所以你的 grep 不依赖于“高度实验性”(参见手册页!) 功能。
  • @EdMorton:谢谢 Ed。是的,我会从下一次开始处理。让我也编辑。你认为哪个更好?
  • PCRE 使用与 BRE 和 ERE 非常不同的算法/正则表达式引擎来适应向前/向后/无论什么,即使您不使用任何特定于 PCRE 的功能,该引擎也会慢得多,因此 BRE 和ERE 比 PCRE 快。详情请见swtch.com/~rsc/regexp/regexp1.html
  • 好的。是的,有道理。 (y)。
【解决方案2】:

使用 GNU awk 进行多字符 RS:

$ awk -v RS='N+' 'RT{print length(RT)}' file
5
8
7

$ awk -v RS='N+' 'RT{print RT, length(RT)}' file
NNNNN 5
NNNNNNNN 8
NNNNNNN 7

【讨论】:

  • 感谢您的帮助,但我没有从您的代码中得到结果。如何使用文件?
  • file 只是一个包含问题中显示的输入字符串的文件。您可以改用echo 'AATGATGGAANNN...' | awk -v RS='N+' 'RT{print length(RT)}'。不过,正如它所说,您必须使用 GNU awk。
  • 这可以打到$0=length(RT)
【解决方案3】:

试试这两个:

第一个

sed 's/[^N]/ /g' file | awk '{for(i=1;i<=NF;i++){print $i":"length($i)}}'

第二个

cat file | tr -c 'N' ' ' | awk '{for(i=1;i<=NF;i++){print $i":"length($i)}}'

【讨论】:

    【解决方案4】:

    短 GNU awk 方法:

    str='AATGATGGAANNNNNGATAGAACGATNNNNNNNNGATAATGANNNNNNNTAGACTGA'
    
    awk -v FPAT='N+' '{for(i=1;i<=NF;i++) print $i,length($i)}' <<< $str
    

    输出:

    NNNNN 5
    NNNNNNNN 8
    NNNNNNN 7
    

    【讨论】:

      【解决方案5】:

      您可以使用awk 拆分每个字符上的字段,而不是N 并打印每个字段及其长度:

      s='AATGATGGAANNNNNGATAGAACGATNNNNNNNNGATAATGANNNNNNNTAGACTGA'
      
      awk -F '[^N]+' '{for (i=1; i<=NF; i++) if ($i != "") print $i, length($i)}' <<< "$s"
      
      NNNNN 5
      NNNNNNNN 8
      NNNNNNN 7
      

      另一种选择是使用grep + awk

      grep -Eo 'N+' <<< "$s" | awk '{print $1, length($1)}'
      

      这里是纯 BASH 解决方案

      shopt -s extglob
      while read -r line; do
          [[ -n $line ]] && echo "$line ${#line}"
      done <<< "${s//+([!N])/$'\n'}"
      
      NNNNN 5
      NNNNNNNN 8
      NNNNNNN 7
      

      BASH 解决方案详情:

      1. 它使用扩展的 glob 模式来匹配 1 个或多个 non-N 字符,并将它们替换为 +([!N])/$'\n'}" 中的换行符
      2. 使用while 循环,我们遍历N 字符的每个子字符串
      3. 在循环中,我们打印每个字符串和该字符串的长度。

      【讨论】:

      • See working demo 你得到了什么输出?
      • 另一个选项是使用:grep -Eo 'N{2,}' &lt;&lt;&lt; "$s" | awk '{print $1, length($1)}'
      • 这有效:awk -F '[^N]+' '{for (i=1; i&lt;=NF; i++) if ($i != "") print length($i)}' &lt;&lt;&lt; "$s"
      • @raam86:答案中添加了详细信息。
      • 没有意识到你指的是前面定义的s,谢谢你的详细解答
      【解决方案6】:

      正如您要求的 sed 解决方案,如果您的重复字符链不超过 9 个字符并且您的字符串不包含任何分号,则可以使用此解决方案:

      sed 's/$/;NNNNNNNNN0123456789/;:a;s/\(N\+\)\([^;]*;\1.\{9\}\)\(.\)\(.*\)/\2\3\4\n\3/;ta;s/[^\n]*\n//'

      【讨论】:

        【解决方案7】:

        这是一个 Perl 单行代码:

        perl -ne 'while (m/(.)(\1*)/g) { printf "%5i %s\n", length($2)+1, $1 }' <<<AATGATGGAANNNNNGATAGAACGATNNNNNNNNGATAATGANNNNNNNTAGACTGA
        2 A
        1 T
        1 G
        1 A
        1 T
        2 G
        2 A
        5 N
        1 G
        1 A
        1 T
        1 A
        1 G
        2 A
        1 C
        1 G
        1 A
        1 T
        8 N
        1 G
        1 A
        1 T
        2 A
        1 T
        1 G
        1 A
        7 N
        1 T
        1 A
        1 G
        1 A
        1 C
        1 T
        1 G
        1 A
        

        m/(.)(\1*)/ 连续匹配尽可能多的相同字符,只要字符串仍然包含我们尚未匹配的内容,/g 就会在下一次迭代中重新开始匹配。所以我们以相同的字符块循环字符串,并且在每次迭代中,打印第一个字符以及整个匹配字符串的长度。

        第一对括号捕获(剩余不匹配的)行开头的字符,\1 表示重复该字符。 * 量词尽可能多地匹配它。

        如果您只对 N:s 感兴趣,您可以将第一个括号更改为 (N),或者您可以添加类似 printf("%7i %s\n", length($2), $1) if ($1 == "N") 的条件。同样,如果您只想要有重复(多次出现)的命中,您可以说 \1+ 而不是 \1* 或添加类似 ... if length($2) &gt;= 1 的条件。

        【讨论】:

          【解决方案8】:

          你可以借助正则表达式的方法。

          这是我从以下链接获得的解决方案代码

          Count occurrences of a char in a string using Bash

          needle=","
          var="text,text,text,text"
          
          number_of_occurrences=$(grep -o "$needle" <<< "$var" | wc -l)
          

          如您所见,借助 WC(字数),我们很容易得到“$needle”的出现次数。

          你可以循环它以满足你的需求。

          【讨论】:

          • @b.nota 我保证,如果您在问题中包含了预期的输出,那么 Kevin 就不会误解您的要求并浪费他的时间发布与您所拥有的不同问题的解决方案(并因为他的麻烦而被否决 - 而不是我)。
          • 我也没有投反对票,感谢这里的所有帮助!
          猜你喜欢
          • 1970-01-01
          • 2018-11-22
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-11-20
          相关资源
          最近更新 更多