ネクストエンジンの取込ファイルが「文字エンコーディングが一致しません」で弾かれたので、実物をバイト単位で調べた
在庫連携用のファイルを取り込もうとしたら、こう言われた。
テキストまたは文字エンコーディングが一致しませんでした。
中身は合っているはずなのに、読んでもらえない。どこが違うのかも書いていない。仕様が公開されていないので、実物から調べることにした。
同じところで詰まる人がいると思うので、結論と調べ方を書いておく。
結論
取込ファイルは、この形式だった。
| 項目 | 値 |
|---|---|
| 拡張子 | .txt(.csv は受け付けない) |
| 文字コード | UTF-8(BOM無し) |
| 区切り文字 | タブ |
| 改行 | CRLF |
| クォート | 無し |
| ヘッダー行 | あり |
BOM付きのUTF-8でも駄目だった。BOMが付くと1列目の列名が壊れて、そんな列は無いと判断される。
最初に何を間違えたか
日本のECツールなので、なんとなくShift-JISだろうと思って出力していた。楽天の一括登録CSVがShift-JIS(CP932)なので、その流れで揃えたつもりだった。
区切り文字も分からなかったから、タブ区切り版とカンマ区切り版の2つを作って両方試した。
両方とも同じエラーで落ちた。全滅だ。
このとき「区切り文字が違うのかな」と考えたのだが、それは的外れだった。両方落ちたということは、区切り文字以外に原因があるという意味だったのに、そこを読み違えている。
正解のファイルを1つ手に入れることにした。既に登録されているデータをエクスポートして送ってもらう。仕様が無くても、動いている実物が1つあれば、そこから読み取れる。
やったのは、テキストエディタで開くのではなく、バイト列として調べることだった。
b = open('実物のファイル.txt', 'rb').read()
print(b[:8]) # 先頭のバイト(BOMがあるか)
print(b.count(b'\r\n')) # CRLF の数
print(b.count(b'\t')) # タブの数
print(b.count(b'"')) # クォートの数
for enc in ['utf-8', 'cp932']:
try:
print(enc, 'OK', b.decode(enc)[:40])
except UnicodeDecodeError:
print(enc, 'NG')
結果はこうだった。
先頭8バイト: b'NE\xe5\x95\x86\xe5\x93\x81' ← BOM無し。UTF-8の日本語
CRLF: 501 / 単独LF: 0
タブ: 3507 / カンマ: 0 / ダブルクォート: 0
utf-8 OK
cp932 NG(UnicodeDecodeError)
先頭がBOM(\xef\xbb\xbf)ではなく、いきなり NE という文字で始まっている。BOM無しだ。そしてShift-JISではデコードできない。ここで自分の間違いが確定した。
テキストエディタでは分からない
なぜ最初からエディタで見なかったのか、と思うかもしれない。見ても分からないからだ。
エディタは文字コードを自動判定して、BOMを表示せず、改行コードも見えない形で表示するので、こちらが見ているのは中身そのものではなく、エディタが親切に整えて見せてくれた解釈済みの姿でしかない。開けたという事実は、形式が合っている証拠にならない。むしろ、開けてしまうせいで問題ないと思い込む。
実際、Pythonで読むときにも一度間違えた。
open(path).read() # ← これだと \r\n が \n に変換されて見える
Pythonはテキストモードで開くと改行を変換する。CRLFかLFかを調べたいのに、調べる過程で変換されていた。バイナリモード('rb')で開く必要がある。
直したあとも、目視では確認しなかった。生成したファイルと実物を、同じ方法で比べた。
実物: BOM無し / CRLF 501 / 単独LF 0 / クォート 0
生成: BOM無し / CRLF 7 / 単独LF 0 / クォート 0
ヘッダー行の一致: True
行数以外が一致していれば、形式としては同じものだ。ここまで確認してからアップロードして、通った。
まとめると、取込ファイルは .txt、UTF-8のBOM無し、タブ区切り、CRLF。日本のツールだからShift-JISだろうという思い込みが、丸一日を溶かした原因だった。
仕様が公開されていない形式は、推測で組み合わせを試すより、正解のファイルを1つもらってバイト列で見る方が速い。試行錯誤だと、今回のように両方落ちたときに何も学べないまま次に進むことになる。
同じエラーで止まっているなら、まずBOMの有無を疑うといいと思う。