区切り文字がコンマであるデータファイルから「」の間のデータを抽出したいと思います。
入力ファイルの例:
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10,000",8/13/2019,
予想出力:
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10000",8/13/2019,
答え1
これが正しい形式のCSVであると仮定すると(例データはこの点で問題ありません)、次のようcsvformat
に使用できます。csvkit
フィールド区切り文字をデータにない他の文字に一時的に変更します。たとえば、@
すべてのカンマを削除してから、フィールド区切り文字をデフォルト値に戻します。
$ csvformat -D '@' file.csv | tr -d , | csvformat -d '@'
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,10000,8/13/2019,
出力には、変更したフィールドの周りに引用符はありませんが、もはや必要ないからです。
明らかに、「すべてのカンマを削除する」は実際に削除したくないカンマを削除することができるため、7番目のフィールドのカンマのみを選択的に削除できます。
$ csvformat -D '@' file.csv | awk -F '@' 'BEGIN { OFS=FS } { gsub(",", "", $7); print }' | csvformat -d '@'
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,10000,8/13/2019,
答え2
別のawk
解決策:
awk -F\" '{
OFS="\"";
for ( i = 1; i <= NF; i++ ) {
if ( i % 2 == 0 ) {
gsub(/,/, "", $i)
}
}
}1' input.csv
次に、二重引用符をフィールド区切り文字として使用し、すべてのフィールドを繰り返します。フィールド番号が偶数の場合(完全ではありませんが、例ではフィールドが引用符の間にあることを意味する必要があります)、フィールドからすべてのコンマが削除されます。これにより、出力フィールド区切り文字で二重引用符を使用してすべての内容が印刷され1
ます(変更)。awk
使用中:
$ cat input.csv
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10,000",8/13/2019,
,7/30/2019,7/31/2019,"100",FH/FN 30yr & 20yr TBA & Spec ,"10,000,000",8/13/2019,
,7/30/2019,7/31/2019,"Jack, Mary, and Jane",8/1/2019,"123,456,789,012,345,678","10,000",8/13/2019,
$ awk -F\" '{
> OFS="\"";
> for ( i = 1; i <= NF; i++ ) {
> if ( i % 2 == 0 ) {
> gsub(/,/, "", $i)
> }
> }
> }1' input.csv
,7/30/2019,7/31/2019,Wed,8/1/2019,FH/FN 30yr & 20yr TBA & Spec ,"10000",8/13/2019,
,7/30/2019,7/31/2019,"100",FH/FN 30yr & 20yr TBA & Spec ,"10000000",8/13/2019,
,7/30/2019,7/31/2019,"Jack Mary and Jane",8/1/2019,"123456789012345678","10000",8/13/2019,
メモ:これ〜する数値以外のフィールドからカンマを削除します。 csvファイルを正しく読み取るには、これを行う必要があります。何らかの理由でカンマを保持するには、次の回避策を使用できます。
awk -F\" '{
OFS="\"";
for ( i = 1; i <= NF; i++ ) {
if ( i % 2 == 0 && $i ~ /[0-9]/ ) {
gsub(/,/, "", $i)
}
}
}1' input.csv
答え3
たとえば、次のようになりますawk
。
cat oldfile | awk '{ print gensub ("(,\"[0-9]+),([0-9][0-9][0-9]),?([0-9][0-9][0-9])?,?([0-9][0-9][0-9]),?","\\1\\2\\3\\4","g");}' > newfile
これは大容量データにも当てはまります。
説明する:
awk
プログラム可能なフィルタです。コマンドライン(外側の一重引用符 "'"の間)で指定されたコマンドは、ファイル内の各入力行に対して実行されます。
awkプログラムは次のとおりです(他の形式):
{
print gensub ("(,\"[0-9]+),([0-9][0-9][0-9]),?([0-9][0-9][0-9])?,?([0-9][0-9][0-9]),?",
"\\1\\2\\3\\4",
"g");
}
-buildinawk
コマンドは、gensub
最初の引数に指定された内容を 2 番目の引数に指定された代替項目に置き換えます。 3番目の引数が「g」または「G」で始まる文字列である場合は、すべての項目を置き換えます(見つからなくなるまで試してください)。
置き換えられるものは何ですか?最初の引数は二重引用符で囲まれた正規表現(qv)です。次の部分は次のとおりです。,\
次に、[0-9]+
1回以上繰り返される数字0〜9を示します(接尾辞演算子+
)。,
これは1文字だけで、その後に[0-9][0-9][0-9]
カンマが,
続き、その後に質問が続きます。表示?
(これは最初の部分が何を意味するのかを知っていますが、サフィックスは?
新しいものです。カンマ数字は省略できます)。その後、より多くの数値グループと省略できるコンマがあります。これはより大きな数字です。
今回の説明ではこれまでかっこ(
を省略しました!)
これは表現と一致しますが、覚えているものを示します。 2番目の引数では、最初から4番目の一致する項目(数字)をgensub
引用し、そこから再印刷します。\1
\4
答え4
sed '/\"/,/\"/s/,//'
指定したアドレス範囲は、行内の範囲ではなく行範囲のみをフィルタリングするため、試行は失敗します。
このタイプの使命は標準では面倒ですsed
。カンマだけがsed -E 's/("[0-9]*),([0-9]*")/\1 \2/
問題を解決しますが、複数のカンマがある場合は繰り返す必要があるため、次のような醜い結果が出ます。
sed -Ee :loop -e 's/("[0-9 ]*),([^"]*")/\1 \2/;tloop'
開いた二重引用符の後にランダムな桁が続き、置換に引用されており、コンマの後の("[0-9]*)
すべての項目と最後まで一致するため、同じですが、最初のカンマを置き換えます。\1
([^"]*")
"
\1 \2
これで交換が行われると、t
コマンドはタグに分岐します。loop
これ以上置き換えるコンマがなくなるまでこれを繰り返します。
これは複数の数字と必要な数のカンマ,7/30/2019,"99,999,999,999,999",0,1 ,"10,000","foo, bar"
としても機能します。,7/30/2019,"99 999 999 999 999" 0 1 "10 000" "foo, bar"