SadServers解説#12 "Saint Paul": Merge Many CSVs files
問題概要
シナリオ
多数の CSV ファイルを結合する
問題詳細
/home/admin/polldayregistrations_enregistjourduscrutin?????.csv
ディレクトリにある338個すべてのファイルを、1つのファイル/home/admin/all.csv
に結合 (マージ) してください。
結合されたファイルは、すべてのCSVファイルの内容を任意の順序で含む必要があります。
列の名前は、ヘッダーとして1行のみ存在する必要があります。
解決判定
解答をファイルall.csv
に書いて、Check My Solution
ボタンをクリックしてください。
解答が正解かどうか、コマンドプロンプト上で確認することも可能です。次のコマンドを実行して、以下と同じ出力が得られた場合は正解です。
$ ./agent/check.sh
OK
問題解決の方針
【表示する】
今回の問題は、ファイル編集です。
問題文をきちんと理解し、ファイルの編集手順を考え、コマンドを実行していきましょう。
解決の手順を表示する
- ファイルの値を確認しながら問題文を正確に理解する
- 題意を満たすようなファイルの編集手順を考える
- ファイルの編集手順をコマンドで実行していく
ヒント
一部、SadServers公式のヒントを改変しています。
ヒント1
問題文を理解するために、ファイルの先頭を覗いてみます。
~$ ls | head
README.txt
agent
polldayregistrations.zip
polldayregistrations_enregistjourduscrutin10001.csv
polldayregistrations_enregistjourduscrutin10002.csv
polldayregistrations_enregistjourduscrutin10003.csv
polldayregistrations_enregistjourduscrutin10004.csv
polldayregistrations_enregistjourduscrutin10005.csv
polldayregistrations_enregistjourduscrutin10006.csv
polldayregistrations_enregistjourduscrutin10007.csv
~$
~$ head -3 polldayregistrations_enregistjourduscrutin1000[123].csv
==> polldayregistrations_enregistjourduscrutin10001.csv <==
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
10001,"Avalon/Avalon","1","Freshwater",10,8,1,106
10001,"Avalon/Avalon","2","Victoria",9,0,4,330
==> polldayregistrations_enregistjourduscrutin10002.csv <==
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
10002,"Bonavista--Burin--Trinity/Bonavista--Burin--Trinity","1","Davidsville",25,0,17,199
10002,"Bonavista--Burin--Trinity/Bonavista--Burin--Trinity","2","Frederickton",25,0,0,136
==> polldayregistrations_enregistjourduscrutin10003.csv <==
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
10003,"Coast of Bays--Central--Notre Dame/Coast of Bays--Central--Notre Dame","1","Tilting",8,0,3,153
10003,"Coast of Bays--Central--Notre Dame/Coast of Bays--Central--Notre Dame","2","Joe Batt's Arm",8,0,2,250
どうやら、ファイルには共通して以下のヘッダーが含まれているようです。
Electoral District Number/Numéro de circonscription,...,Electors/Électeurs
これを踏まえると、ファイルを以下のように編集して結合すればよいはずです。
ファイル | 編集内容 |
---|---|
polldayregistrations_enregistjourduscrutin10001.csv | そのまま |
polldayregistrations_enregistjourduscrutin10002.csv | ヘッダー行を削除 |
︙ | ︙ |
polldayregistrations_enregistjourduscrutin62001.csv | ヘッダー行を削除 |
どうすれば、上記のような操作ができるでしょうか。
ヒント2
ファイルを結合してから先頭行だった行を削除すればよいように思えますが、そうすると一つ目のファイルの先頭行まで消えてしまいます。
そのため、今回は、先に先頭行を取っておいて、上の処理をしたファイルに先頭行をくっつける作戦でいきます。
まとめると、今回は以下の流れでファイルを編集します。
1.ファイルから先頭行を取り出す
2.ファイルを結合する
3.ファイルの先頭行だった行を削除する
4.手順1と手順3で作成したファイルを結合する
ヒント3
1.ファイルから先頭行を取り出す
ファイルから先頭行を取り出すには、head
コマンドを使います。
実行コマンド
~$ head -1 polldayregistrations_enregistjourduscrutin10001.csv > header
~$ cat header
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
所望の結果が得られました。
2.ファイルを結合する
ファイルを結合するには、cat
コマンドを使います。
実行コマンド
~$ cat polldayregistrations_enregistjourduscrutin*.csv > body
3.ファイルの先頭行だった行を削除する
ファイルの特定の行を削除するには、sed
コマンドを使います。
実行コマンド
文章には/
やアクセント符号のついた文字などが含まれていて処理が大変なので、ファイルの先頭一致した行を削除してしまいましょう。
先頭一致で置換して問題ないか確認するために、"Electoral" で始まる行がbody
ファイルに何種類含まれているか確認しておきます。
~$ grep Electoral body | uniq
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
どうやら、"Electoral" で始まる行は、もともと先頭行だった行だけのようです。
body
ファイルから、"Electoral" で始まる行を削除しましょう。
~$ sed -i '/Electoral/d' body
~$ grep Electoral body
~$
body
ファイルから、"Electoral" で始まる行がなくなりました。
4.1と3のファイルを結合する
ファイルを結合するには、手順2 と同様にcat
コマンドを使います。
実行コマンド
~$ cat header body > all.csv
これで、ファイルの編集は完了したはずです。
一応、軽く中身を見ておきます
実行コマンド
~$ head all.csv
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
10001,"Avalon/Avalon","1","Freshwater",10,8,1,106
10001,"Avalon/Avalon","2","Victoria",9,0,4,330
10001,"Avalon/Avalon","3","Victoria",13,1,4,454
10001,"Avalon/Avalon","4","Victoria",17,0,0,342
10001,"Avalon/Avalon","5","Victoria",13,3,5,358
10001,"Avalon/Avalon","6","Carbonear",6,0,8,440
10001,"Avalon/Avalon","7","Carbonear",4,1,6,302
10001,"Avalon/Avalon","8","Carbonear",9,0,1,295
10001,"Avalon/Avalon","9","Carbonear",2,2,4,364
~$ grep Electoral all.csv
Electoral District Number/Numéro de circonscription,Electoral District Name/Nom de circonscription,Polling Station Number/Numéro du bureau de scrutin,Polling Station Name/Nom du bureau de scrutin,Additions/Ajouts,Corrections/Corrections,Deletions/Suppressions,Electors/Électeurs
all.csv は、"Electoral" で始まる行が先頭で、その後はcsv形式の行が続いています。また、"Electoral" で始まる行は1行だけです。
どうやら、想定通りのファイルができていそうです。
別解
SadServersのヒントでは、awk
コマンドを使う方法が紹介されています。
awkコマンドについては以下の記事で解説しています。
(記事誠意作成中です)
Sadserversが今回の問題で想定しているコマンドについて解説します。
そのコマンドは以下です。
awk 'FNR==1 && NR!=1 {next} {print}' polldayregistrations_*.csv > all.csv
【解説】
polldayregistrations_???.csvのファイルの各行に対して、以下の処理をしています。
-
FNR(各ファイル内の行番号)が1、かつNR(通しの行番号)が1ではない
→現在のレコードをスキップ ({next}
) -
それ以外
→現在のレコードを出力 ({print}
)
よって、各ファイルへのコマンドの動作は以下となります。
-
最初のファイル
-
FNR==1 && NR!=1
は偽となるため、{print}
が実行され、最初のファイルのすべての行が出力される
-
-
2番目以降のファイル
- 最初の行:
FNR==1 && NR!=1
が真となるため、{next}
が実行され、最初の行(ヘッダー行)はスキップされる - 2行目以降:
FNR==1 && NR!=1
は偽となるため、{print}
が実行され、2行目以降が出力される
- 最初の行:
「いきなり問題を解き始めても調べるばかりになってしまう…」 「やりたいことが分かっても、コマンドが分からない…」 という方は、下記の記事でLinuxのコマンドを復習してから、SadServersの問題に取り掛かってみてはいかがでしょうか。
問題一覧はこちら
Discussion