ファイルの6行目ごとに5行目の値を合計します。

ファイルの6行目ごとに5行目の値を合計します。

次のテキストブロックを含むtxtファイルがあります。

17-01-2023
Purchase AAA
Apple Pay John Doe
Full Payment
-11,34€
0,11€
30-01-2023
Purchase BBB
Mastercard Jane Doe
Installment
-23,90€
0,24€

したがって、日付、購入タイプ、支払いタイプと名前、支払いタイプ、負の値、割引が順番に表示されます。

これは、何千ものエントリを含むファイルで繰り返されます。

値の合計(この場合は11,34 + 23,90)を取得し、その合計を正数にしたいと思います。数字の後にユーロ記号があり、私のロケールでは、カンマが小数点区切り記号であることを覚えておいてください。

sed、awkなどを使用して端末でこれを行うにはどうすればよいですか?

答え1

awkを使用できます。数字以外の通貨記号接頭辞(たとえば)がある場合とは異なり、€-23,90数値変換中に数字以外の接尾辞は無視されます。実装によっては、ロケールの小数点区切り文字を異なる方法で処理できます。

mawk 'NR%6 == 5 {sum -= $0} END {print sum}' file

尊重LC_NUMERIC/LC_ALL要求に従ってPOSIX準拠、GNU awkはデフォルトでPOSIX仕様から離れていますが、ロケールを使用するように指示する必要があります。

gawk --use-lc-numeric 'NR%6 == 5 {sum -= $0} END {print sum}' file

GNU Awkユーザーガイドを参照してください。ロケールは変換に影響します


たとえば、de_DE.UTF-8ロケールを使用してテストします。

$ export LC_NUMERIC=de_DE.UTF-8
$ 
$ mawk 'NR%6 == 5 {sum -= $0} END {print sum}' yourfile
35,24
$ 
$ gawk --use-lc-numeric 'NR%6 == 5 {sum -= $0} END {print sum}' sum=x yourfile
35,24

Mac OSの場合:

$ awk --version
awk version 20200816

$ export LC_NUMERIC=de_DE.UTF-8

$ awk 'NR%6 == 5 {sum -= $0} END {print sum}' yourfile
35,24

答え2

これはただ楽しみのためのものです。 GNU sedを使用してビルドするとしますn~m

$ sed -n '5~6{y/-,€/_.+/;p}' file | dc -e0 -f- -e_1\*p
35.24

(もちろん、必要に応じて他のsedまたはtrを追加して小数点を元のロケールに戻すこともできます,)。

答え3

取引金額が常にレコードの5行目にあると仮定すると、レコードの先頭は「DD-MM-YYYY」形式の日付で表され、このパターンはレコードの先頭にのみ発生する可能性があります。次のawkプログラムは次のことを行います。

awk -v dpt=$(locale decimal_point) '/^([[:digit:]]{2}-){2}[[:digit:]]{4}$/{line_of_rec=0}
     {if (++line_of_rec==5) { if (dpt==".") sub(/,/,"."); total-=$0 } }
     END{printf "Total payments: %.2f\n",total}' input.txt

仕組みは次のとおりです。

  • コマンドの結果をプログラムにlocale decimal_point変数として渡しますdpt。これはawk、小数点区切り文字を使用して入力書式を設定する設定にあるように見えますが、ロケールは使用中に設定され、数字,の小数部分が欠落している.ためです。awk
  • パターン認識を通じて出発線を記録し(前後にスペースがないと仮定します)、変数をline_of_record0に設定します。
  • 各行に対してline_of_recordカウンタを増やします。 5に達したら、行の内容が少数として解釈されるように(必要な場合),に置き換え、変数から行の内容を減算して正の支払い金額を合計するようにします。.awktotal
  • 行末で合計が印刷されます。

これは必要な最小値よりも多くのコードですが、空行があるとプログラムがより強力になります。別々の記録(まだ記録の5行にある取引金額に依存します。)

答え4

使用幸せ(以前のPerl_6)

タイプチェックなし:

~$ raku -ne 'state $sum1; $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5; END say $sum1;'  file

#OR 

raku -e 'my $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5 for lines; say $sum1;'  file

パスタイプの確認:

~$ raku -ne 'state Rat $sum1; $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5; END say $sum1;'  file

#OR

~$ raku -e 'my Rat $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5 for lines; say $sum1;'  file

簡単に言えば、最初の例では、Rakuは-neコマンドラインから自動印刷ではなくフラグで実行されます。スカラー変数はd$sum1です。これは、stateフラグ付きのループが始まる-ne前にインスタンス化されることを意味します。 2番目のステートメントで、匿名増分行カウンター変数モジュロを6++$で割った値が5の場合、点にカンマが追加され、ユーロ記号が削除されます(削除されません)。次に変数に追加します。ループの終わりに。%trans,.subst+=$sumENDsay $sum1

入力例:

17-01-2023
Purchase AAA
Apple Pay John Doe
Full Payment
-11,34€
0,11€
30-01-2023
Purchase BBB
Mastercard Jane Doe
Installment
-23,90€
0,24€

出力例:

-35.24

累積合計の場合、say変数を累積するだけです。

~$ raku -ne 'state $sum1; say $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5;'

出力例:

-11.34
-35.24

@AdminBeeの回答に触発され、行にawk文字が含まれている場合にのみ行カウンタを増やすことで、レコード間に空白行を許可できます。.chars

~$ raku -ne 'state Rat $sum1; say $sum1 += $_.trans("," => ".").subst(/\€/) if .chars && ++$ % 6 == 5;'

&&出力例(上記で使用したものと同じand):

-11.34
-35.24

OPの例に示されている数字は、基本的にRatRakuではイオン数として入力されます(Rakuで利用可能な他のタイプにはNumsとIntsが含まれています)。RatRakuのs(十分に小さいと仮定)は、通常、丸めエラーが発生せず、すぐに小数に変換されます。たとえば、ENDステートメントを次のように変更します。

~$ raku -ne 'state $sum1; $sum1 += $_.trans("," => ".").subst(/\€/) if ++$ % 6 == 5; END say $sum1.numerator, "/", $sum1.denominator;'

出力例:

-881/25

より速い作業のためにをsay $sum1.nude返します(-881 25)

https://docs.raku.org/言語/numerics.html#Rational
https://raku.org

関連情報