はじめに
1999年に発売されたDelphi5で作成されたレガシーアプリケーションがあり、サーバーリプレース作業に伴いデータベースもOracle→PostgreSQLに変更になることで改修作業をしました。
Delphi5では標準でUTF-8に対応していないため、外部コンポーネントを使用してUTF-8のファイルを読み込みと保存をするのですが、UTF-8のBOM付きしか対応していません。
PostgreSQLではCSVファイルをインポートする際、UTF-8のBOM無しにする必要がありまして、BOMを削除するのに下記サイトを参考にしました。
utf8-bom.cmd BOMありUTF-8ファイル BOMなしUTF-8ファイル
start /min /wait cmd /c chcp.com 65001 ^& cmd /u /c type %1 ^>$$$ ^& cmd /c type $$$ ^>%2 ^& del $$$
文字化け
データ量が少ないファイルなら問題がなかったのですが、データ量が多いと幾つか文字化けが発生しているところが出てきました。
文字化けしている文字に問題があるのかと思ったのですが、ごく普通の文字「ャ、ー、ン、ウ、鳥、オ、中、野」で、別行のところでは正常に変換されているのです。
文字化けした行に正常に変換されている文字にしてみたところ文字化けしたので、桁位置に問題がありそうです。ただ文字化けした行位置に特に規則性がありませんでした。
データ量としては5600バイトを超えたあたりから幾つかの文字化けが発生します。
文字化けした部分をバイナリーエディターで見たところ、「EF BF BD」がセットされていました。
この文字列は「謎の文字列 %EF%BF%BD」の記事によると「REPLACEMENT CHARACTER」でUTF-8でエンコード/デコードして失敗したときに使われる文字のようです。
対応
バッチからPowerShellを呼び出して、ワンライナーでUTF-8のBOM無しに変換するようにしました。
参照:UTF-8 BOM付きでファイルを、BOMなしで保存する。
powershell -NoProfile -ExecutionPolicy Unrestricted -Command "& { $MyPath = 'test.csv';$MyFile = Get-Content $MyPath; $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False);[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)}"
複数ファイル(例 csv形式)を一括変換する場合
@echo off
cd /d %~dp0
for %%a in (*.csv) do (
powershell -NoProfile -ExecutionPolicy Unrestricted -Command "& { $MyPath = '%%a';$MyFile = Get-Content $MyPath; $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False);[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)}"
)
最後に
このPostgreSQL対応自体は2013年に行ったものでしたが当時は使われることがなく、今回のサーバーリプレース作業により本格的に使用するようになって問題が判明することになりました。
一番の問題はユーザーから指摘されたことですね。データ量によって文字化けするとは思いもしてなかったのでテスト漏れしてしまった。