次のテキストブロックを含む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_record
0に設定します。 - 各行に対して
line_of_record
カウンタを増やします。 5に達したら、行の内容が少数として解釈されるように(必要な場合),
に置き換え、変数から行の内容を減算して正の支払い金額を合計するようにします。.
awk
total
- 行末で合計が印刷されます。
これは必要な最小値よりも多くのコードですが、空行があるとプログラムがより強力になります。別々の記録(まだ記録の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
€
+=
$sum
END
say $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の例に示されている数字は、基本的にRat
Rakuではイオン数として入力されます(Rakuで利用可能な他のタイプにはNum
sとInt
sが含まれています)。Rat
Rakuの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