各行が特定の建物の部屋を定義するcsvファイルがあります。部屋に加えて、各列には床フィールドがあります。抽出したいのは、すべての建物のすべてのフロアです。
私のファイルは次のようになります...
"u_floor","u_room","name"
0,"00BDF","AIRPORT TEST "
0,0,"BRICKER HALL, JOHN W "
0,3,"BRICKER HALL, JOHN W "
0,5,"BRICKER HALL, JOHN W "
0,6,"BRICKER HALL, JOHN W "
0,7,"BRICKER HALL, JOHN W "
0,8,"BRICKER HALL, JOHN W "
0,9,"BRICKER HALL, JOHN W "
0,19,"BRICKER HALL, JOHN W "
0,20,"BRICKER HALL, JOHN W "
0,21,"BRICKER HALL, JOHN W "
0,25,"BRICKER HALL, JOHN W "
0,27,"BRICKER HALL, JOHN W "
0,29,"BRICKER HALL, JOHN W "
0,35,"BRICKER HALL, JOHN W "
0,45,"BRICKER HALL, JOHN W "
0,59,"BRICKER HALL, JOHN W "
0,60,"BRICKER HALL, JOHN W "
0,61,"BRICKER HALL, JOHN W "
0,63,"BRICKER HALL, JOHN W "
0,"0006M","BRICKER HALL, JOHN W "
0,"0008A","BRICKER HALL, JOHN W "
0,"0008B","BRICKER HALL, JOHN W "
0,"0008C","BRICKER HALL, JOHN W "
0,"0008D","BRICKER HALL, JOHN W "
0,"0008E","BRICKER HALL, JOHN W "
0,"0008F","BRICKER HALL, JOHN W "
0,"0008G","BRICKER HALL, JOHN W "
0,"0008H","BRICKER HALL, JOHN W "
私が欲しいのは、すべての建物のすべてのフロアです。
Cat、awk、sort、uniqを使用してこのリストを取得していますが、「BRICKER HALL、JOHN W」などの建物名フィールドの「、」に問題があり、csv生成全体が破棄されています。
cat Buildings.csv | awk -F, '{print $1","$2}' | sort | uniq > Floors.csv
Awkにコンマを使用してフィールドの「」の間にあるコンマを無視させるにはどうすればよいですか?あるいは、誰かがより良い解決策を持っていますか?
Awk csvパーサーを提案する回答に基づいて、私は解決策を得ることができました:
cat Buildings.csv | awk -f csv.awk | awk -F" -> 2|" '{print $2}' | awk -F"|" '{print $2","$3}' | sort | uniq > floors.csv
そこで、 csv awk プログラムを使用し、そこから "-> 2 |"を使用したいこれは、csv awkプログラムに基づいたフォーマットです。そこのprint $ 2は、csvで解析された内容のみを出力します。これは、プログラムが元の行の後に "->#"を出力するためです。#はcsvから解析されたカウントです。 (つまり、列。)そこから、このawk csvの結果を「|」で分割できます。 whcihは、コンマを置き換えるものです。次に、ソート、uniq、およびファイルへのパイプアウトを行います!
助けてくれてありがとう。
csv.awk
から得られる追加の出力は、デモコードからのものです。スクリプト内の関数を使用して解析を実行し、必要に応じて出力することを意図しています。
csv.awk
の最後は、関数の1つを示す{ ... }
ループです。 -> 2|
を出力しているのはそのコードです。
その代わりに、解析関数を呼び出してprint csv[1], csv[2]
を実行するだけです。
コードのその部分は次のようになります。
{
num_fields = parse_csv($0, csv, ",", "\"", "\"", "\\n", 1);
if (num_fields < 0) {
printf "ERROR: %s (%d) -> %s\n", csverr, num_fields, $0;
} else {
# printf "%s -> ", $0;
# printf "%s", num_fields;
# for (i = 0;i < num_fields;i++) {
# printf "|%s", csv[i];
# }
# printf "|\n";
print csv[1], csv[2]
}
}
your_script
(たとえば)として保存します。
chmod +x your_script
を実行します。
cat
は不要です。また、sort -u
の代わりにsort | uniq
を実行できます。
コマンドは次のようになります。
./yourscript Buildings.csv | sort -u > floors.csv
_gawk -vFPAT='[^,]*|"[^"]*"' '{print $1 "," $3}' | sort | uniq
_
これはすごいGNU Awk 4の拡張機能で、フィールドセパレータパターンの代わりにフィールドパターンを定義します。CSVについては不思議です。( docs )
ETA(mitchusに感謝):周囲の引用符を削除するには、gsub("^\"|\"$","",$3)
;そのように処理するためのフィールドが_$3
_だけではない場合、それらをループします。
この単純なアプローチは、不正な形式の入力や引用符の間のいくつかの可能な特殊文字を許容しないことに注意してください。
私の回避策は、次を使用してcsvからコンマを取り除くことです:
decommaize () {
cat $1 | sed 's/"[^"]*"/"((&))"/g' | sed 's/\(\"((\"\)\([^",]*\)\(,\)\([^",]*\)\(\"))\"\)/"\2\4"/g' | sed 's/"(("/"/g' | sed 's/"))"/"/g' > $2
}
つまり、最初に開き引用符を「((」で、閉じ引用符を「))」で置き換え、次に「(( "whatever、whatever"))」を「whateverwhatever」で置き換えてから、「((」の残りのすべてのインスタンスを変更します。 "))" 戻る "。
このawkbased csv paserを試すことができます:
私が書いたcsvquoteというスクリプトを使用して、引用されたフィールド内のコンマをawkに無視させることができます。コマンドは次のようになります。
csvquote Buildings.csv | awk -F, '{print $1","$2}' | sort | uniq | csvquote -u > Floors.csv
これはawkよりカットが少し簡単かもしれません:
csvquote Buildings.csv | cut -d, -f1,2 | sort | uniq | csvquote -u > Floors.csv
ここでcsvquoteコードを見つけることができます: https://github.com/dbro/csvquote
問題は実際にはCSVフィールド内のコンマとフィールドを区切るコンマを区別することなので、さらに解析しやすくするために、最初の種類のコンマを別のものに置き換えることができます。
0,"00BDF","AIRPORT TEST "
0,0,"BRICKER HALL<comma> JOHN W "
このgawkスクリプト(replace-comma.awk)は次のことを行います。
BEGIN { RS = "(.)" }
RT == "\x022" { inside++; }
{ if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }
これは、実際のレコード区切りをRT
という変数にキャプチャするgawk機能を使用します。すべての文字をレコードに分割し、レコードを読みながら、引用符内にあるコンマ(\x022
)<comma>
。
[〜#〜] fpat [〜#〜] ソリューションは、エスケープされた引用符と引用符内のコンマの両方がある特別なケースで失敗しますが、このソリューションはすべてのケースで機能します。
§ echo '"Adams, John ""Big Foot""",1' | gawk -vFPAT='[^,]*|"[^"]*"' '{ print $1 }'
"Adams, John "
§ echo '"Adams, John ""Big Foot""",1' | gawk -f replace-comma.awk | gawk -F, '{ print $1; }'
"Adams<comma> John ""Big Foot""",1
簡単なコピー&ペーストのためのワンライナーとして:
gawk 'BEGIN { RS = "(.)" } RT == "\x022" { inside++; } { if (inside % 2 && RT == ",") printf("<comma>"); else printf(RT); }'
Perlの_Text::CSV_XS
_などの本格的なCSVパーサーは、そのような奇妙さを処理するために特別に設計されています。
Perl -MText::CSV_XS -lne 'BEGIN{$csv=Text::CSV_XS->new()} if($csv->parse($_)){ @f=$csv->fields(); print "$f[0],$f[1]" }' file
入力行は配列_@f
_に分割されます
フィールド1は_$f[0]
_です。これは、Perlが0でインデックス付けを開始するためです
出力:
_u_floor,u_room
0,00BDF
0,0
0,3
0,5
0,6
0,7
0,8
0,9
0,19
0,20
0,21
0,25
0,27
0,29
0,35
0,45
0,59
0,60
0,61
0,63
0,0006M
0,0008A
0,0008B
0,0008C
0,0008D
0,0008E
0,0008F
0,0008G
0,0008H
_
私はここで私の答えの中で_Text::CSV_XS
_の詳細な説明を提供しました: gawkを使用してcsvファイルを解析する