✂️

SadServers解説#11 "Minneapolis": Break a CSV file

2024/11/04に公開

問題概要

シナリオ

CSVファイルを分割する

問題詳細

home/admin/ディレクトリにあるCSVファイルdata\.csvを、サイズがほぼ同じになるように正確に10個の小さなファイルdata-00.csvdata-01.csv、…data-09.csvに分割してください。すべてのファイルは、data\.csvと同じヘッダー(カラム名が書かれた1行目)を持つ必要があります。どの小さなファイルも32KBを超えてはいけません。

※ファイルが行の途中で区切られても構いません(つまり、ファイルは改行で区切らず、どこでも分割して構いません)。生成されるファイルは、完全なCSVファイルである必要はありません。

解決判定

Check My Solutionボタンをクリックしてください。
解決判定をするスクリプト/home/admin/agent/check.shは閲覧・実行可能です。

 

問題解決の方針

【表示する】

今回は、ファイルの分割などの方法が問われています。
均等に分割しなくてもよい代わりに、ヘッダーをそろえる必要があるそうです。
題意を満たすファイルを作成するためにはどういった段取りが必要でしょうか。

解決の手順を表示する
  1. data\.csvを分割する
  2. 分割したファイルの1行目にdata\.csvの1行目を挿入する

先にdata\.csvから1行目を切り出し、残りの部分を10分割してから1行目を挿入することで、各ファイルのサイズを均等にすることができます。別解にその解法を載せています。

 

ヒント

一部、SadServers公式のヒントを改変しています。

ヒント1

ファイルを分割するコマンドは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
                            output file is opened
      --help     display this help and exit
      --version  output version information and exit

The SIZE argument is an integer and optional unit (example: 10K is 10*1024).
Units are K,M,G,T,P,E,Z,Y (powers of 1024) or KB,MB,... (powers of 1000).
Binary prefixes can be used, too: KiB=K, MiB=M, and so on.

CHUNKS may be:
  N       split into N files based on size of input
  K/N     output Kth of N to stdout
  l/N     split into N files without splitting lines/records
  l/K/N   output Kth of N to stdout without splitting lines/records
  r/N     like 'l' but use round robin distribution
  r/K/N   likewise but only output Kth of N to stdout

GNU coreutils online help: <https://www.gnu.org/software/coreutils/>
Report any translation bugs to <https://translationproject.org/team/>
Full documentation <https://www.gnu.org/software/coreutils/split>
or available locally via: info '(coreutils) split invocation'

 
…だそうなので、-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

xaaxajの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を下回っていることが確認できます。
Check My Solutionボタンをクリックしてクリアです!

補足:headerの挿入のイメージ図

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

 

問題一覧はこちら

https://zenn.dev/comf_nakamura/articles/sadservers_sitemap

Discussion