Macで濁点が別文字になる問題の原因と解決法(NFCとNFDの違い)
2分で読める
テック
最終更新:
Webアプリケーション開発中に、日本語タイトルの検索機能を実装したとき、濁点を含む文字列だけ検索がヒットしない問題に遭遇しました。原因はMacのNFD正規化でした。
起きたこと
検索フォームの入力文字列とデータベースのタイトルを照合する実装で、アルファベットや英数字は問題なく動いていたのに、「ブログ」「プログラム」のような濁点・半濁点を含む文字列だけ一致しませんでした。
Macの濁点・半濁点問題
Macのファイルシステム(APFSおよびHFS+)は、日本語の濁点・半濁点をNFD形式で処理します。
NFD形式では、例えば「ブ」を「フ」+「゛」(2文字の組み合わせ)として扱います。一方、多くのデータベースやOSでは「ブ」を1文字(NFC形式)として扱います。この不一致が検索ミスの原因です。
補足: macOS 10.13 High Sierra以降のデフォルトはAPFSですが、この濁点問題はAPFSでも同様に発生します。
NFC/NFDとは
UnicodeはNFC・NFDという2種類の「正規化形式」を定めています。
- NFC(Normalization Form Canonical Composition): 「が」を1つの合成済み文字として扱う
- NFD(Normalization Form Canonical Decomposition): 「が」を「か」+「゛」の2文字として扱う
同じ文字列でもNFCとNFDでバイト列が異なるため、単純な文字列比較では一致しません。
対処法
検索時に文字列をNFCに正規化してから比較します。
Pythonの場合:
import unicodedata
def normalize(text: str) -> str:
return unicodedata.normalize("NFC", text)
# 使用例
query = normalize(request.params["q"])
Rubyの場合:
# ActiveSupportを使う場合
query = params[:q].unicode_normalize(:nfc)
# 標準ライブラリを使う場合
query = params[:q].unicode_normalize
JavaScriptの場合:
const query = input.normalize("NFC");
入力値とデータベースの値の両方を同じ正規化形式に揃えることで、OS差異に依存しない文字列比較ができます。
記事の更新をメールで受け取る
質問・リクエストを送る
記事についての質問や、取り上げてほしいテーマがあればお気軽にどうぞ。いただいた質問はブログ記事として回答し、Q&Aページで公開することがあります。