awk 配列を使用する場合は、問題の大きな数値を合計し、すべての小数点で結果を印刷します。

awk 配列を使用する場合は、問題の大きな数値を合計し、すべての小数点で結果を印刷します。

以下には、3番目の列の日付に基づいて複数のファイルに分割する必要がある入力ファイルがあります。デフォルトでは、同じ日付のすべての取引は特定の日付のファイルに分割する必要があります。分割したら、タイトルと予告編を作成する必要があります。予告編の4番目の列には、レコード数と金額の合計(その日の金額の合計)を含める必要があります。このような場合、上記のように数字が非常に大きいのですが、下のコードでbcをどのように統合できますか?

入力ファイル

H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-06T12:33:52.27|~^123562388.23456|~^2018-04-12T12:33:52.27|~^hhh
R|~^abc|~^2019-03-05T12:33:52.27|~^105603.042|~^2018-10-23T12:33:52.27|~^aus
R|~^abc|~^2019-03-05T12:33:52.27|~^2054.026|~^2018-10-24T12:33:52.27|~^usa
R|~^abc|~^2019-03-06T12:33:52.27|~^10.00|~^2018-09-11T12:33:52.27|~^virginia
R|~^abc|~^2019-03-05T12:33:52.27|~^30.00|~^2018-08-05T12:33:52.27|~^ddd
R|~^abc|~^2019-03-06T12:33:52.27|~^15.03|~^2018-10-23T12:33:52.27|~^jjj
R|~^abc|~^2019-03-06T12:33:52.27|~^10.04|~^2018-04-08T12:33:52.27|~^jj
R|~^abc|~^2019-03-05T12:33:52.27|~^20.00|~^2018-07-23T12:33:52.27|~^audg
T|~^20200425|~^8|~^xxx|~^123670130.37256

出力ファイル 20190305.txt

H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-05T12:33:52.27|~^105603.042|~^2018-10-23T12:33:52.27|~^aus
R|~^abc|~^2019-03-05T12:33:52.27|~^2054.026|~^2018-10-24T12:33:52.27|~^usa
R|~^abc|~^2019-03-05T12:33:52.27|~^30.00|~^2018-08-05T12:33:52.27|~^ddd
R|~^abc|~^2019-03-05T12:33:52.27|~^20.00|~^2018-07-23T12:33:52.27|~^audg
T|~^20200425|~^4|~^xxx|~^107707.068

出力ファイル 20190306.txt

H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-06T12:33:52.27|~^123562388.23456|~^2018-04-12T12:33:52.27|~^hhh
R|~^abc|~^2019-03-06T12:33:52.27|~^10.00|~^2018-09-11T12:33:52.27|~^virginia
R|~^abc|~^2019-03-06T12:33:52.27|~^15.03|~^2018-10-23T12:33:52.27|~^jjj
R|~^abc|~^2019-03-06T12:33:52.27|~^10.04|~^2018-04-08T12:33:52.27|~^jj
T|~^20200425|~^4|~^xxx|~^123562423.30456

私が使用しているコード(PS:コミュニティメンバーの1人が提案する)awk解決策は次のとおりです。

awk -F'\\|~\\^' '{ 
            if($1=="H"){ 
                head=$0
            }
            else if($1=="T"){
                foot=$1"|~^"$2
                foot4=$4
            }
            else{
                date=$3;
                sub("T.*","", date);
                data[date][NR]=$0;
                sum[date]+=$4; 
                num[date]++
            }
           }
           END{
            for(date in data){
                file=date".txt";
                gsub("-","",file); 
                print head > file; 
                for(line in data[date]){
                    print data[date][line] > file
                } 
                printf "%s|~^%s|~^%s|~^%s\n", foot, num[date], 
                                              foot4, sum[date] > file
            }
           }' file 

コードは非常にうまく動作します。しかし、この段階では

sum[date]+=$4;

大きな数を合わせることはできません。最後のステップで使用しているため、%sトレーラーの合計は指数値で印刷されます。

printf "%s|~^%s|~^%s|~^%s\n", foot, num[date], 
                                                  foot4, sum[date] > file

ここでは、大きな数字に合計を適用し、正確な合計を印刷したいと思います。 (ここでbc(bash電卓)を試しましたが、合計が配列に基づいて特定の日付に基づいて追加されるため停止しました。)この問題を解決するのに役立ちます

また、"%.15g"トレーラーのステップを試しました。

printf "%s|~^%s|~^%s|~^%.15g\n", foot, num[date], 
                                                  foot4, sum[date] > file

ここで結果が15桁(小数点を含む)であれば、正確な合計を求めることができます。合計結果が15桁を超える場合、この方法は効果がありません。助けてください

ベストアンサー1

awk大きな数字の問題を無視して、次のプログラムを作成します。

BEGIN {
        FS = "\\|~\\^"
        OFS= "|~^"
}

$1 == "H" {
        header = $0
}

$1 == "R" {
        name = $3
        sub("T.*", "", name)

        sum[name] += $4
        cnt[name] += 1

        if (cnt[name] == 1)
                print header >name ".txt"

        print >name ".txt"
}

$1 == "T" {
        for (name in sum)
                print $1, $2, cnt[name], $4, sum[name] >name ".txt"
}

便宜上、出力フィールド区切りOFS文字を|~^。これにより、出力フィールド間に挿入することを心配する必要がなくなります。入力フィールド区切り文字FSは、この文字列に一致する正規表現に設定されます。

次に、3つの主要なコードブロックがあります。

  1. 1つは行を解析するために使用されますH。そのうちの1つだけがあり、最初に発生すると仮定します。これは単にヘッダー行を変数に格納しますheader

  2. 一つは、Rライン解析のためのものです。各レコードには、3番目のフィールドで出力ファイル名として使用する必要がある日付が含まれています。それはあなたと同じように解析されます。その日付の合計が累積され、カウンタが増加します。

    カウンタが1の場合、つまり特定の日付を最初に見た場合は、その日付に関連する出力ファイルにヘッダを書き込みます。次に、現在のレコードをファイルに書き込みます。

  3. 最後のブロックは行をT解析します。そのうちの1つだけがあり、最後に表示されるとします。これは、単に各個々の日付の累積合計と数を、元の行の一部のデータTとともにその日付に関連するファイルに出力します。

ランダムに大きな数字をサポートします(例:他の場所で数字を保存するのに100ビット以上が必要でオーバーフローする場合(の整数)、任意精度計算機を「コプロセス」(コンピューティングサービス)awkとして使用します。bc行はsum[name] += $4次に置き換えられます。

if (sum[name] == "") sum[name] = 0
printf "%s + %s\n", sum[name], $4 |& "bc"
"bc" |& getline sum[name]

これにはGNU awk(ほとんどのUnixシステムで何らかの方法で利用可能)が必要です。

これが行うことは、現在の日付の合計がまだない場合は、まず現在の日付の合計をゼロに初期化することです。私たちがこれを行う理由は、初期合計を提供する必要があるからです0bc

bc次に、awkGNU固有のパイプを使用して評価する必要がある式を印刷して|&補助プロセスに書き込みます。このbcユーティリティは、スクリプトと並列に起動し、実行され、計算を実行し、awkgetlineのパイプの出力を直接 。bc|&sum[name]

私が理解しているように、GNUは各合計ごとに別々のプロセスを作成するのではなく、awk共同プロセスとして実行されるプロセスを維持します。したがって、これはローカルで計算を実行するよりも遅いですが、合計ごとに別々の計算を生成するよりもはるかに高速です。bcbcawkbc

与えられたデータに対して、次の2つのファイルが生成される。

$ cat 2019-03-05.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-05T12:33:52.27|~^105603.042|~^2018-10-23T12:33:52.27|~^aus
R|~^abc|~^2019-03-05T12:33:52.27|~^2054.026|~^2018-10-24T12:33:52.27|~^usa
R|~^abc|~^2019-03-05T12:33:52.27|~^30.00|~^2018-08-05T12:33:52.27|~^ddd
R|~^abc|~^2019-03-05T12:33:52.27|~^20.00|~^2018-07-23T12:33:52.27|~^audg
T|~^20200425|~^4|~^xxx|~^107707.068
$ cat 2019-03-06.txt
H|~^20200425|~^abcd|~^sum
R|~^abc|~^2019-03-06T12:33:52.27|~^123562388.23456|~^2018-04-12T12:33:52.27|~^hhh
R|~^abc|~^2019-03-06T12:33:52.27|~^10.00|~^2018-09-11T12:33:52.27|~^virginia
R|~^abc|~^2019-03-06T12:33:52.27|~^15.03|~^2018-10-23T12:33:52.27|~^jjj
R|~^abc|~^2019-03-06T12:33:52.27|~^10.04|~^2018-04-08T12:33:52.27|~^jj
T|~^20200425|~^4|~^xxx|~^123562423.30456

おすすめ記事