AWK替换除一位数之外的所有数字

时间:2014-07-01 10:16:58

标签: regex awk split gsub

我有这种格式的文件:

A,C 0|1 0|2 1|2

我现在需要的是:

A   0|1 0|0 1|0
C   0|0 0|1 0|1

所以,我首先分割线条,这不是问题。

awk 'BEGIN{FS=OFS="\t"}{n=split($1,obs,",");for (i=1;i<=n;i++){$1=obs[i];print}}'

但现在我需要删除第一行中的2并删除第二行中的1,甚至将第二行中的2替换为1。

我希望这是可以理解的。数字指的是A(1)和C(2)。 我的想法是使用gsub并将所有数字替换为零,除了当前行的数字。

但是有两个问题我无法解决:

1.以下不起作用(第2行):

awk 'BEGIN{FS=OFS="\t"}{n=split($1,obs,",");for (i=1;i<=n;i++){$1=obs[i];for(j=2;j<=NF;j++){gsub(/[1-9[^2]]/,0,$j)};print}}'

这不应该匹配所有数字但是2?

2.即使这样可行,我还需要用变量(当前行号)替换正则表达式中的数字。

有关于此的任何想法吗?

编辑: 这可能更复杂: 输入

A,C,G,GA    0|1 0|2 1|2 2|3 4|0

期望的输出:

A   0|1 0|0 1|0 0|0 0|0
C   0|0 0|2 0|1 1|0 0|0
A   0|0 0|0 0|0 0|1 0|0
GA  0|0 0|0 0|0 0|0 1|0

4 个答案:

答案 0 :(得分:1)

对于一般情况:

awk '{
    n = split($1,a,",")
    rest = substr($0, length($1)+1)
    for (i=1; i<=n; i++) {
        regex = "[0-" i-1 i+1 "-9]"
        x = rest
        gsub(regex, "0", x)
        gsub(i, "1", x)
        print a[i], x
    }
}' << END
A,C,G,GA    0|1 0|2 1|2 2|3 4|0
END
A     0|1 0|0 1|0 0|0 0|0
C     0|0 0|1 0|1 1|0 0|0
G     0|0 0|0 0|0 0|1 0|0
GA     0|0 0|0 0|0 0|0 1|0

答案 1 :(得分:0)

试试这一行:

 awk -F"\t", -v OFS="\t" 
    '{n=split($1,a,",")}n==2{$1=a[1];t=$0;gsub(/2/,"0",t);print t;
                             $1=a[2];gsub(/1/,"0");gsub(/2/,"1");print}' file

这只处理X,Y个案例,即$1中的一对。

测试,(在我的测试f中,它是空格分隔的,而不是<tab>,所以没有-F.. -v OFS...):

kent$  cat f
A,C 0|1 0|2 1|2

kent$  awk  '{n=split($1,a,",")}n==2{$1=a[1];t=$0;gsub(/2/,0,t);print t;$1=a[2];gsub(/1/,"0");gsub(/2/,"1");print}' f
A 0|1 0|0 1|0
C 0|0 0|1 0|1

答案 2 :(得分:0)

假设第二个示例输入的示例输出中的杂散2是错误的(它应该是1),那么问题是转换输入:

A,C,G,GA    0|1 0|2 1|2 2|3 4|0

到所需的输出:

A   0|1 0|0 1|0 0|0 0|0
C   0|0 0|1 0|1 1|0 0|0
A   0|0 0|0 0|0 0|1 0|0
GA  0|0 0|0 0|0 0|0 1|0

结合另一个例子,列表中只有两个项目,后面有3对数字,很明显输出中的列数不固定。

AFAICT,目标是将第一个字段拆分为N个字段,然后为N个字段中的每个字段生成一行输出。当数字不是 n 时,字段 n 的输出行包含0,当数字 n 时,输出行包含1。为简单起见,假设列表中不会有超过9个项目(如果数字可以有两位数,则必须使用更复杂的正则表达式。)

翻译成awk,即成为:

awk '{ N = split($1, code, ",")
       $1 = ""
       tail = $0
       for (i = 1; i <= N; i++)
       {
           line = code[i] "  " tail
           for (j = 1; j <= N; j++)
           {
               if (j != i)
                   gsub(j, "0", line)
           }
           gsub(i, "1", line)
           print line
       }
     }'

除了间距之外,这会从给定输入产生所需的输出。可以通过以下方式“固定”间距(如果需要修复):

awk '{ N = split($1, code, ",")
       $1 = ""
       tail = $0
       for (i = 1; i <= N; i++)
       {
           line = tail
           for (j = 1; j <= N; j++)
           {
               if (j != i)
                   gsub(j, "0", line)
           }
           gsub(i, "1", line)
           printf("%-4s %s\n", code[i], line
       }
     }'

答案 3 :(得分:0)

这是我的“自己的”解决方案,退出类似于Jonathan Leffer的解决方案:

awk 'BEGIN{FS=OFS="\t"}{
    line=$0
    n=split($1,obs,",")
    for (i=1;i<=n;i++){
        $0=line
        $1=obs[i]
        for(j=2;j<=NF;j++){
            for(k=1;k<=n;k++){
                if(k!=i){
                    gsub(k,"0",$j)
                    gsub(i,"1",$j)
                }
            }
        }
        print
    }
}'