SadServers解説 Easy No.11 「CSVファイルを分割する」
問題概要
問題URL
シナリオ
CSVファイルを分割する
問題詳細
/home/admin/ディレクトリにあるCSVファイルdata\.csvを、サイズがほぼ同じになるように10個の小さなファイルdata-00.csv、data-01.csv、…data-09.csvに分割してください。すべてのファイルは、data\.csvと同じヘッダー(カラム名が書かれた1行目)を持つ必要があります。どの小さなファイルも32KBを超えてはいけません。
※ファイルが行の途中で区切られても構いません(つまり、行の途中で分割しても構いません)。
解決判定
Check My Solutionボタンをクリックしてください。
解決判定をするスクリプト/home/admin/agent/check.shは閲覧・実行可能です。
問題解決の方針
【表示する】
今回は、ファイルの分割などの方法が問われています。
均等に分割しなくてもよい代わりに、ヘッダーをそろえる必要があるそうです。
題意を満たすファイルを作成するためにはどういった段取りが必要でしょうか。
具体的な解決の段取りを表示する
-
data\.csvを分割する - 分割したファイルの1行目に
data\.csvの1行目を挿入する
先にdata\.csvから1行目を切り出し、残りの部分を10分割してから1行目を挿入することで、各ファイルのサイズを均等にすることもできます。別解にその解法を載せています。
ヒント
オリジナルヒント
ヒント1
ファイルを分割するコマンドはsplitです。
実行コマンド
splitというコマンドの使い方がわからないので、まずはhelpを見てみます。
$ split --help
Usage: split [OPTION]... [FILE [PREFIX]]
Output pieces of FILE to PREFIXaa, PREFIXab, ...;
default size is 1000 lines, and default PREFIX is 'x'.
With no FILE, or when FILE is -, read standard input.
Mandatory arguments to long options are mandatory for short options too.
-a, --suffix-length=N generate suffixes of length N (default 2)
--additional-suffix=SUFFIX append an additional SUFFIX to file names
-b, --bytes=SIZE put SIZE bytes per output file
-C, --line-bytes=SIZE put at most SIZE bytes of records per output file
-d use numeric suffixes starting at 0, not alphabetic
--numeric-suffixes[=FROM] same as -d, but allow setting the start value
-x use hex suffixes starting at 0, not alphabetic
--hex-suffixes[=FROM] same as -x, but allow setting the start value
-e, --elide-empty-files do not generate empty output files with '-n'
--filter=COMMAND write to shell COMMAND; file name is $FILE
-l, --lines=NUMBER put NUMBER lines/records per output file
-n, --number=CHUNKS generate CHUNKS output files; see explanation below
-t, --separator=SEP use SEP instead of newline as the record separator;
'\0' (zero) specifies the NUL character
-u, --unbuffered immediately copy input to output with '-n r/...'
--verbose print a diagnostic just before each
...
…だそうなので、-n 10オプションで10ファイルに分割しましょう。
$ split -n 10 data.csv
$ ls -l
total 640
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaa
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xab
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xac
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xad
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xae
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaf
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xag
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xah
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xai
-rw-r--r-- 1 admin admin 31276 Oct 13 06:02 xaj
xaa~xajの10つのファイルに分割されたようです。
ヒント2
分割したファイルの1行目に、data.csvの1行目を挿入しましょう。
まずは、data.csvの1行目を抽出して、headerというファイルを作成しましょう。
実行コマンド
$ head -1 data.csv > header
$ ls -l
total 640
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 372 Oct 13 06:05 header
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaa
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xab
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xac
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xad
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xae
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaf
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xag
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xah
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xai
-rw-r--r-- 1 admin admin 31276 Oct 13 06:02 xaj
headerというファイルが作成できました。
中身が心配なら、元のファイルとdiffを取るなどしてください。
続けて、分割したファイルの1行目にheaderを挿入します。
実行コマンド2
一つ目のファイルには、すでにヘッダーが含まれているため、headerファイルと結合する必要がないことに注意してください。
$ cat xaa > data-00.csv
$ cat header xab > data-01.csv
$ cat header xac > data-02.csv
$ cat header xad > data-03.csv
$ cat header xae > data-04.csv
$ cat header xaf > data-05.csv
$ cat header xag > data-06.csv
$ cat header xah > data-07.csv
$ cat header xai > data-08.csv
$ cat header xaj > data-09.csv
$ ls -l
total 1280
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 31271 Oct 13 06:09 data-00.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-01.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-02.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-03.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-04.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-05.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-06.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-07.csv
-rw-r--r-- 1 admin admin 31643 Oct 13 06:09 data-08.csv
-rw-r--r-- 1 admin admin 31648 Oct 13 06:09 data-09.csv
-rw-r--r-- 1 admin admin 372 Oct 13 06:05 header
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaa
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xab
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xac
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xad
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xae
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xaf
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xag
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xah
-rw-r--r-- 1 admin admin 31271 Oct 13 06:02 xai
-rw-r--r-- 1 admin admin 31276 Oct 13 06:02 xaj
各ファイルが32KBを下回っていることが確認できます。
$ /home/admin/agent/check.sh
OK
補足:headerの挿入のイメージ図

別解
先にdata.csvから1行目を切り出し、残りの部分を10分割してから1行目を挿入します。
これにより、各ファイルのサイズを均等にすることができます。
実行コマンド
ファイルの結合をシェルで行うため、xa, xb,...ではなく01, 02,...と採番されるように、-dオプションを使用しています。
$ head -n 1 data.csv > header
$ tail -n +2 data.csv > body
$ cat header body > test #想定通りに分割できていることを確認
$ diff data.csv test
$
$ ls -l
total 936
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312343 Oct 13 07:30 body
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 372 Oct 13 07:28 header
-rw-r--r-- 1 admin admin 312715 Oct 13 07:30 test
$
$ split -n 10 -d body
$
$ ls -l
total 1256
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312343 Oct 13 07:30 body
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x00
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x01
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x02
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x03
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x04
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x05
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x06
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x07
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x08
-rw-r--r-- 1 admin admin 31237 Oct 13 07:31 x09
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 372 Oct 13 07:28 header
-rw-r--r-- 1 admin admin 312715 Oct 13 07:30 test
$
$ for i in {0..9}; do cat header x0$i > data-0$i.csv; done
$
$ ls -l
total 1576
-rw-r--r-- 1 admin admin 422 Jul 20 16:49 README.txt
drwxr-xr-x 2 admin root 4096 Jul 20 16:49 agent
-rw-r--r-- 1 admin admin 312343 Oct 13 07:30 body
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x00
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x01
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x02
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x03
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x04
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x05
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x06
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x07
-rw-r--r-- 1 admin admin 31234 Oct 13 07:31 x08
-rw-r--r-- 1 admin admin 31237 Oct 13 07:31 x09
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-00.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-01.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-02.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-03.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-04.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-05.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-06.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-07.csv
-rw-r--r-- 1 admin admin 31606 Oct 13 07:32 data-08.csv
-rw-r--r-- 1 admin admin 31609 Oct 13 07:32 data-09.csv
-rw-r--r-- 1 admin admin 312715 Jul 20 16:49 data.csv
-rw-r--r-- 1 admin admin 372 Oct 13 07:28 header
-rw-r--r-- 1 admin admin 312715 Oct 13 07:30 test
$ ./agent/check.sh
OK
SadServers公式ヒント(翻訳)
ヒント1
-
splitコマンドを確認してみましょう。man splitを見てください。
ヒント2
-
split -d -n 10 data.csv data-を実行すると、data.csvは 31KB 前後のdata-00からdata-09の 10 ファイルに分割されます。ただし、CSV のヘッダーを持つのはdata-00だけです。
ヒント3
- すべての小さいファイルにヘッダーを付けるには、まず
head -1 data.csv > header.txtでヘッダーをファイルに保存します。
その後、for i in {0..9}; do cat header.txt data-0$i > data-0$i.csv; doneを実行します。
参考
split -n 10 はファイルをバイト数が均等になるように10分割します。デフォルトのサフィックスはアルファベット(xaa, xab, ...)ですが、-d オプションで数値(x00, x01, ...)に切り替えられます。
ヘッダーを持つCSVを分割する場合、今回のように「先に分割してからヘッダーを付け足す」方法と、「別解」で示した「ヘッダーとデータを分離してからデータだけを分割し、最後にヘッダーを結合する」方法があります。後者のほうが各ファイルのサイズが揃いやすくなります。
「いきなり問題を解き始めても調べるばかりになってしまう…」 「やりたいことが分かっても、コマンドが分からない…」 という方は、下記の記事でLinuxのコマンドを復習してから、SadServersの問題に取り掛かってみてはいかがでしょうか。
問題一覧はこちら
Discussion