首页 > 解决方案 > 使用 awk 提取信息

问题描述

这篇文章与我之前关于字符串拆分的问题有关:Awk split string into words and numbers。假设我们有以下字符串:

1A5T4

该字符串对以下信息进行编码: A 在位置 2(A 之前的 1 个项目) T 在位置 8(T 之前的 7 个项目,即 1 + A + 5) 没有更多的字母超过最右边的一个意味着没有更多的相关信息要提取。

所以这里想要的输出是A T 2 8

我想编写 Awk 脚本来获取此信息,最好在两个数组中:一个包含位置,另一个包含字母。我认为这将是一种方便的存储方式,因为我需要使用我正在编写的脚本的其他部分中的值(或者更确切地说是努力编写)。

我认为第一步是通过拆分字符串来分隔字符串(感谢有帮助的评论者Awk split string into words 和 numbers)。

echo 1A5T4 | awk '{gsub(/[^0-9]+/," & ")}1'
1 A 5 T 4

但也许分隔符不是必需的。我尝试使用 for 循环完成任务,通过迭代连续的字母数字对,并将它们添加到数组中。但是,我无法使其工作(没有任何问题,因为我无法让循​​环正常工作):

echo 1A5T4 | awk '{gsub(/[0-9]+$/,"", $0); a = $0}{for (i = 1; i <= length(a); i++2) {b = substr(a, i, 1) + 1 + b; print b}}'
        2
        3
        9
        10

*这里的想法是只获取数字,然后在单独的 for 循环中获取字母

我也有这样扩展字符串的想法:.A.....T....然后通过计算从开头到字母的字符串长度来获取字母的位置。

我需要处理的字符串将包含一个更复杂的内容 - 另一种类型的块:插入符号后跟一组字母。在此块中,插入符号后面的字母数将添加到最终索引中。下面的例子:

1A2^CCG3T4

A 为 2(如上例所示) T 为 11(2 + 2 + 3(CCG 中插入符号后的字母总和)+ 3,因此 T 之前有 10 个位置)

所以这里想要的输出是A T 2 11

插入符号后面的字母与其他任何内容无关,除了将字母的索引移动到插入符号块的速率。

获得一些有关如何解决此问题的有用提示会很棒。

澄清:脚本应该输出所有字母,只要它们前面没有插入符号。插入符号后的字母仅移动索引。例如:

27T19T^A16G8G29

应该给

 T T G G 28 48 66 75

27T19T16G8G29

应该给

 T T G G 28 48 65 74

更新:

感谢@vgersh99,我设法改进了代码。它首先将每个跟随的文本块转换为与其他块相同的格式。然后以相同的方式处理所有块(for循环),最后,插入符号值不显示(if语句)。但是,如果有多个可变长度的插入符号块,问题仍然存在。

1A5T4
1A1^AAAAA2T2
1A2^CCG3T4
27T19T^A16G8G29
27T19T16G8G29
1A^AA5^TT4T4
10A3A1G9A10A25^TT1^G1^G42T12^G1G29

{
  match($0, /\^[A-Z]+/);
  a = "^"length(substr($0, RSTART, RLENGTH))-2"^";
  gsub(/\^[A-Z]+/, a)
}
# if a letter is directly followed by a caret, such carets are removed, as they would have count==0
{
  a = match($0, /[A-Z]+\^/);
  a = substr($0, RSTART, RLENGTH-1);
  gsub(/[A-Z]+\^/, a)
}
# intermediate string with transformed caret blocks is then used further
  {
      sum=0; delete(out); str=""
      n=patsplit($0,b, /[[:alpha:]^]/, seps);
      for(i=1; i<=n;i++) {
        sum+=seps[i-1]+1
        # print b[i], sum
        if (b[i]!="^")
        {out[sum]=b[i]}
      }

    PROCINFO["sorted_in"] = "@ind_num_asc"
    for(i in out) {
      printf("%s ", out[i])
      str=(str? str OFS:"") i
  }

      print str
    } tst.txt

A T 2 8
A T 2 12
A T 2 12
T T G G 28 48 66 75
T T G G 28 48 65 74
A T 2 17
A A G A A T G 11 15 17 27 38 117 134

最后一行的最后两个值不正确,应该是 112 和 127。

这是因为 gsub 总是使用第一个匹配来获取字符串的替换,因此中间字符串中的所有替换都是相同的:

10A3A1G9A10A25^1^1^1^1^1^42T12^1^1G29

标签: stringawk

解决方案


% echo 1A5T4 | gawk 'BEGIN{ FS=""; }{ for (i=1;i<=NF;i++) { if($i>="A"){ s=s $i } else { for(j=1;j<=$i;j++)s=s "." }} print s }'
.A.....T....
% echo 1A2^CCG3T4 | gawk 'BEGIN{ FS=""; }{ for (i=1;i<=NF;i++) { if($i>="A"){ s=s $i } else { for(j=1;j<=$i;j++)s=s "." }} print s }'
.A..^CCG...T....
%

也许插入符号处理是错误的,但这不应该太难修复......


推荐阅读