テキストのパース


comp.lang.awk ではテキストファイルのパースとなっていますが、実際には面倒な問題です。

3     // Number of blocks

five lines of text we don't care about
1  23.5
2  34.2
3  45.1
4  52.4

five lines of text we don't care about
1  24.2
2  27.8

five lines of text
1  26.3
2  34.5
3  45.2
4  52.1
5  36.5

というファイルがある場合に、以下のような出力を得るというものです。

1  (23.5+24.2+26.3)/3
2  (34.2+27.8+34.5)/3
3  (45.4+45.2)/2
4  (52.4+52.1)/2
5  36.5

つまり '$1' が同じもの同士を平均にするというもののようです。

/^=+$/ {
        block = !block

        if (!block) {
                n = asorti(a, copy)

                for (i = 1; i <= n; i++) {
                        k = gsub(/\+/, "+", a[copy[i]]) + 1

                        if (k > 1)
                                printf "%d  (%s)/%d\n", i, a[copy[i]], k
                        else
                                printf "%d  %s\n", i, a[copy[i]]
                }
        }

        print

}

block && /^[0-9]+  -?[0-9]+(\.[0-9]+)?$/ {
        if ($1 in a)
                a[$1] = a[$1] "+" $2
        else
                a[$1] = $2

}

最初のものは asorti() を使って、ソートしつつ $1 が何個あるかを数え上げているものです。

NF == 0 { nline = 0 }   # line number within block

++nline > 5 && NF == 2 {
        counter = $1; value = $2
        A[counter] = A[counter] " " value
        if (counter > maxcounter)
                maxcounter = counter

}

END {
        for (counter = 1; counter <= maxcounter; counter++)
        {
                sum = 0
                nf = split(A[counter], Arr)
                for (i = 1; i <= nf; i++)
                        sum += Arr[i]
                mean = sum/nf
                printf("%d %.1f\n", counter, mean)
        }
}
awk '
NF==2 {
        val[$1] = val[$1] sep[$1] $2; sep[$1]="+"; cnt[$1]++;
        max = ($1 > max ? $1 : max)
}

END {
        for (c=1;c<=max;c++)
             if (c in cnt)
                if (cnt[c] > 1)
                        printf "%d\t(%s)/%d\n",c,val[c],cnt[c]
                else
                        printf "%d\t%s\n",c,val[c]

}'
/^[0-9]/ && NF==2{  x[$1] = x[$1] " " $2 }
END{
        for (i in x){
                gsub("^ +","",x[i])      # remove the first space
                y = gsub(" "," + ",x[i]) # add '+' between numbers
                # choose the format and print data
                fmt = (y==0)? "%d  %s\n" : "%d  (%s)/ %d\n"
                printf fmt,i,x[i],y+1 | "sort -n"  # bad idea ?
        }

}
2==NF && /^[0-9]+[ \t]+[0-9.]+$/ {
  a[$1] += $2
  b[$1]++
}

END {
  for (i=1; i in a; i++)
    print i, a[i] / b[i]
}

最後の William James のものは計算してしまっているのですが、出力が複雑なものは計算させた方が楽だったりしますよね。

あとは、以下のようなものもありました。

NF==0 {line=0}
++line>5 && NF>0 {
    sum[$1] += $2;
    counter[$1]++;
}

END {
    for (i=1; i in sum; ++i) print i, sum[i]/counter[i]
}