小技

日々の仕事の中で調べたり発見したりしたことを書き留めていくブログ

q で正しいカラム名を指定しても "no such column" というエラーが出るときの対処

q コマンドで CSV ファイルの解析を行っていたところ、下記のようなエラーが出力されることがありました。

$ q -O -H -d , 'select timestamp, username, remotehost from test.csv'
query error: no such column: timestamp
Warning - There seems to be a "no such column" error, and -H (header line) exists. Please make sure that you are using the column names from the header line and not the default (cXX) column names. Another issue might be that the file contains a BOM. Files that are encoded with UTF8 and contain a BOM can be read by specifying `-e utf-9-sig` in the command line. Support for non-UTF8 encoding will be provided in the future.

カラム名の間違いではないのか

カラム名を間違えたのかな?と思い確認してみると。。

$ q -O -H -d , 'select * from test.csv'
timestamp,username,remotehost
1675857600,foo,192.168.0.1
1675861200,bar,192.168.0.2
1675818000,baz,192.168.0.3
$ cat test.csv
timestamp,username,remotehost
1675857600,foo,192.168.0.1
1675861200,bar,192.168.0.2
1675818000,baz,192.168.0.3

timestamp というカラム名は確かに存在しているようです。だけど、実際にカラム名を指定するとエラーになってしまう。

$ q -O -H -d , 'select username, remotehost from test.csv'
username,remotehost
foo,192.168.0.1
bar,192.168.0.2
baz,192.168.0.3

timestamp というカラム名以外はどうやら問題ありません。 となると、何か別の問題があるのかもしれません。

何が問題なのか

エラーメッセージの後半に注目してみます。

Another issue might be that the file contains a BOM. Files that are encoded with UTF8 and contain a BOM can be read by specifying `-e utf-9-sig` in the command line. Support for non-UTF8 encoding will be provided in the future.

UTF8 でエンコードされていて、"BOM" を含むファイルを読むためには、オプションの指定が必要とのこと。 BOM とは何なのかを調べてみます。

(Wikipediaより)

バイト順マーク (バイトじゅんマーク、英: byte order mark) あるいはバイトオーダーマークとは、通称BOM(ボム)といわれるUnicodeの符号化形式で符号化したテキストの先頭につける数バイトのデータのことである。このデータを元にUnicodeで符号化されていることおよび符号化の種類の判別に使用する。

BOM とはどうやら、Unicode のテキストの先頭に付与されて、符号化の種類の判別に使用されるデータのようです。

cat コマンドでは出力されないのに、本当にそんなデータが入っているのか? od コマンドでダンプしてみます。

$ od -tx1z test.csv
0000000 ef bb bf 74 69 6d 65 73 74 61 6d 70 2c 75 73 65  >...timestamp,use<
0000020 72 6e 61 6d 65 2c 72 65 6d 6f 74 65 68 6f 73 74  >rname,remotehost<
0000040 0a 31 36 37 35 38 35 37 36 30 30 2c 66 6f 6f 2c  >.1675857600,foo,<
0000060 31 39 32 2e 31 36 38 2e 30 2e 31 0a 31 36 37 35  >192.168.0.1.1675<
0000100 38 36 31 32 30 30 2c 62 61 72 2c 31 39 32 2e 31  >861200,bar,192.1<
0000120 36 38 2e 30 2e 32 0a 31 36 37 35 38 31 38 30 30  >68.0.2.167581800<
0000140 30 2c 62 61 7a 2c 31 39 32 2e 31 36 38 2e 30 2e  >0,baz,192.168.0.<
0000160 33 0a                                            >3.<
0000162

確かに、3 バイト分のデータが入っています。 0xEF 0xBB 0xBF は、どうやら UTF-8 の BOM のようです。

BOM 付きのファイルを読み込む方法

エラーメッセージにあるように、BOM が含まれているのが原因のようなので、オプション指定すれば読み込めるはず。

$ q -O -H -d , -e utf-9-sig 'select timestamp from test.csv'
Encoding utf-9-sig could not be found

utf-9-sig というエンコーディングは見つからないといわれています。 確かに。 UTF8 ではないのか?

q - Text as Dataq - Text as Data によると、下記の記載があるので、どうやら -e utf-8-sig とするのが正しいようです。

Files with BOM: Files which contain a BOM (Byte Order Mark) are not properly supported inside python's csv module. q contains a workaround that allows reading UTF8 files which contain a BOM - Use -e utf-8-sig for this. I plan to separate the BOM handling from the encoding itself, which would allow to support BOMs for all encodings.

実際に指定すると、エラーなく読み込むことができました。

$ q -O -H -d , -e utf-8-sig 'select timestamp from test.csv'
timestamp
1675857600
1675861200
1675818000

ファイルから BOM を取り除く方法

読み込むときにオプションを指定してもいいですが、毎回指定するのも手間なので、取り除いておいてオプションなしでも読み込めるようにしたいところです。 sed コマンドで置換することで取り除くことができます。

$ sed -e $'1s/^\xef\xbb\xbf//' test.csv > test2.csv
$ od -tx1z test2.csv
0000000 74 69 6d 65 73 74 61 6d 70 2c 75 73 65 72 6e 61  >timestamp,userna<
0000020 6d 65 2c 72 65 6d 6f 74 65 68 6f 73 74 0a 31 36  >me,remotehost.16<
0000040 37 35 38 35 37 36 30 30 2c 66 6f 6f 2c 31 39 32  >75857600,foo,192<
0000060 2e 31 36 38 2e 30 2e 31 0a 31 36 37 35 38 36 31  >.168.0.1.1675861<
0000100 32 30 30 2c 62 61 72 2c 31 39 32 2e 31 36 38 2e  >200,bar,192.168.<
0000120 30 2e 32 0a 31 36 37 35 38 31 38 30 30 30 2c 62  >0.2.1675818000,b<
0000140 61 7a 2c 31 39 32 2e 31 36 38 2e 30 2e 33 0a     >az,192.168.0.3.<
0000157

BOM を取り除いた後は、-e utf-8-sig オプションなしでも読み込むことができます。

$ q -O -H -d , 'select timestamp from test2.csv'
timestamp
1675857600
1675861200
1675818000

参考