Windows + Docker で PostgreSQL のエンコーディング問題に詰まった話
はじめに
Windows 環境で Docker を使って PostgreSQL を動かしていると、日本語のデータがうまく扱えないことがあります。
「文字化け」「エンコーディングエラー」「Illegal multibyte sequence」
これらのエラーに遭遇し、解決するまでに数時間詰まりました。
この記事は、その詰まりポイントと解決策の記録です。
遭遇した問題
問題1:CSVインポート時の文字化け
dvdrental の日本語化作業で、映画タイトルを日本語に変換したCSVを作成し、PostgreSQLへインポートしようとしました。
COPY film(title) FROM '/tmp/titles_ja.csv' WITH (FORMAT CSV, HEADER TRUE, ENCODING 'UTF8');
エラー:
ERROR: invalid byte sequence for encoding "UTF8": 0x83
WindowsのExcelで保存したCSVが「Shift-JIS(CP932)」だったのが原因です。
問題2:PowerShell からの SQL 実行でクエスチョンマーク
$sql = "UPDATE film SET title = '進撃の巨人' WHERE film_id = 1;"
docker exec -i postgres psql -U postgres -d dvdrental -c $sql
結果: title が ??? になる
PowerShellのデフォルトエンコーディングがUTF-8ではなく、パイプ経由でPostgreSQLに渡る際に変換が起きていました。
解決策
解決策1:CSVをUTF-8(BOMなし)で保存する
Excelで「CSV UTF-8」で保存しても、Windowsでは BOM付きUTF-8(UTF-8 with BOM)になることがあります。
PostgreSQLのCOPYコマンドはBOMを正しく扱えない場合があります。
方法A: VS CodeでCSVを開いて「UTF-8」(BOMなし)で保存
- ファイル → 名前を付けて保存 → エンコード「UTF-8」を選択
方法B: PowerShellで変換
# BOM付きをBOMなしに変換
$content = Get-Content -Path "titles_ja_bom.csv" -Encoding UTF8
$content | Set-Content -Path "titles_ja_nobom.csv" -Encoding UTF8NoBOM
解決策2:PostgreSQLコンテナにコピーしてインポート
# ファイルをコンテナにコピー
docker cp .\sql\titles_ja.csv postgres:/tmp/titles_ja.csv
# コンテナ内で COPY を実行(エンコーディング問題が起きにくい)
docker exec postgres psql -U postgres -d dvdrental -c `
"COPY film(title) FROM '/tmp/titles_ja.csv' WITH (FORMAT CSV, HEADER TRUE, ENCODING 'UTF8')"
ローカルファイルシステムを経由しないため、OSのエンコーディング変換を挟まずに済みます。
解決策3:PowerShell のエンコーディング設定
# スクリプトの先頭に追加
[Console]::OutputEncoding = [System.Text.Encoding]::UTF8
$PSDefaultParameterValues['Out-File:Encoding'] = 'utf8NoBOM'
# または環境変数で設定
$env:PYTHONUTF8 = "1" # Pythonスクリプトを使う場合
PowerShell 7.x(pwsh)はデフォルトがUTF-8ですが、Windows PowerShell 5.1はデフォルトがShift-JISです。
確認方法:
[System.Text.Encoding]::Default
Shift-JIS または Code page 932 と表示される場合、設定変更が必要です。
解決策4:Docker Compose でlocaleを明示設定
# compose.yml
services:
postgres:
image: postgres:16
environment:
POSTGRES_DB: dvdrental
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
LANG: ja_JP.UTF-8
LC_ALL: ja_JP.UTF-8
volumes:
- pgdata:/var/lib/postgresql/data
PostgreSQLのデータベースを作成するときのデフォルトロケールがUTF-8になります。
PostgreSQL のエンコーディング確認
-- 接続中のDBのエンコーディング確認
SELECT pg_encoding_to_char(encoding), datcollate FROM pg_database WHERE datname = current_database();
-- サーバー全体のエンコーディング確認
SHOW server_encoding;
SHOW client_encoding;
server_encoding が UTF8 であることを確認します。
CSVファイルのエンコーディング確認方法
# PowerShell でファイルの先頭バイトを確認
$bytes = [System.IO.File]::ReadAllBytes(".\titles_ja.csv") | Select-Object -First 4
$bytes | ForEach-Object { "0x{0:X2}" -f $_ }
# BOM付きUTF-8 の場合: 0xEF 0xBB 0xBF ...
# BOMなしUTF-8 の場合: データの先頭バイトが直接来る
文字化けしたデータの修正
既に文字化けしたデータが入ってしまった場合の修正:
-- 文字化けデータの確認
SELECT film_id, title FROM film WHERE title ~ '[^\x00-\x7F]' IS FALSE;
-- 正しい日本語データで上書き(CSVから再インポート)
TRUNCATE TABLE film_title_ja; -- 作業用テーブルを使う場合
-- または UPDATE で直接修正
UPDATE film SET title = '正しい日本語タイトル' WHERE film_id = 1;
まとめ
| 問題 | 原因 | 解決策 |
|---|---|---|
| CSVインポート失敗 | Shift-JISまたはBOM付きUTF-8 | UTF-8(BOMなし)で保存し直す |
| PowerShellの文字化け | コンソールエンコーディングがCP932 | [Console]::OutputEncoding = UTF8 |
| コンテナに直接渡せない | PowerShellのパイプが変換する | docker cp でファイルをコピーしてから実行 |
| DBのデフォルトがUTF-8でない | Docker ComposeのLANG未設定 | LANG: ja_JP.UTF-8 を明示 |
Windows開発環境では「UTF-8は当然」という前提が通用しない場面が多いです。
CSVを操作するたびにエンコーディングを確認するクセを付けると、無駄な時間を大幅に削減できます。
このシリーズの記事マップ
→ dvdrental 管理アプリと対になるエンドユーザー向けDVDレンタルアプリを作っている話 — Vue 3 + Spring Boot の全体構成と記事マップ
→ PostgreSQL のサンプル DB dvdrental をベースに Spring Boot + Thymeleaf で DVD レンタル管理アプリを作った話