AIに文字列の整形コードを作らせるときは、「表記ゆれを直す」を具体的な変換へ分けます。全角・半角をそろえる、結合文字を整える、空白を削る、大文字へ変えることは、同じ処理ではありません。まとめて実行すると、区別していた値まで同じになる場合があります。
この記事では、架空のラベル10件をPythonでNFC・NFKCへ正規化し、元の値と結果を並べます。AIが生成したコードや実台帳の処理結果ではありません。行を削除・統合せず、比較キーが重なる箇所を確認する例です。
目次
NFCとNFKCを、見た目の統一だけで選ばない
Unicodeは、正規等価と互換等価に基づく正規化を区別しています。NFCは正規分解と合成、NFKCは互換分解と合成を使うため、NFKCでは全角・半角などの区別が折りたたまれる場合があります。意味に必要な区別を失う可能性もあるので、任意の文章へ無条件に適用しません。Unicode:正規化形式(新しいタブで開く)
ここでは、どちらが優れているかを決めず、同じ入力に対する差を調べます。正式名称や識別子へ何を許すかは、その列の定義へ戻ります。文章を読みやすくする依頼と、システムが照合するキーの変更を一緒に確定しないようにしてください。
表は横にスクロールできます
| 最初に決めること | 今回の扱い |
|---|---|
| 元の値 | original列を保持する |
| 比較する変換 | NFC、NFKCを別列にする |
| 空白処理 | 前後空白を削らない |
| 大小文字 | 変換しない |
| 似た記号の置換 | 独自の置換を追加しない |
| 同じ結果の行 | 候補として表示し、削除しない |
見た目が近い入力を、コードポイントで区別する
次の10件は、実際の品番や顧客名ではない架空のラベルです。R06は「カ+結合濁点+イ+ト+結合濁点」で作ります。R07の「ガイド」と似て見えても、正規化前の文字列は異なります。
表は横にスクロールできます
| ID | 入力の説明 | NFCの結果 | NFKCの結果 |
|---|---|---|---|
| R01 | ABC-123 | ABC-123 | ABC-123 |
| R02 | ABC-123 | ABC-123 | ABC-123 |
| R03 | 部品① | 部品① | 部品1 |
| R04 | 部品1 | 部品1 | 部品1 |
| R05 | ガイド | ガイド | ガイド |
| R06 | カ+U+3099+イ+ト+U+3099 | ガイド | ガイド |
| R07 | ガイド | ガイド | ガイド |
| R08 | ABC−123。区切りはU+2212 | ABC−123 | ABC−123 |
| R09 | 「ABC-123 」の末尾に空白1文字 | 末尾空白が残る | 末尾空白が残る |
| R10 | abc-123 | abc-123 | abc-123 |
R03の丸数字は、NFKCでは通常の数字へ変わります。それがこの列で許されるかは別判断です。「部品①」と「部品1」が別の正式ラベルなら、比較キーが同じだからという理由で原文を統一できません。
R08のマイナス記号は、この例ではASCIIハイフンへ変わりません。NFKCを使えば似た記号がすべて同じになる、と考えないでください。末尾空白の除去や小文字の大文字化も、今回の正規化だけでは行っていません。
元の列を残す小さなコードで試す
Pythonのunicodedata.normalizeでは、形式名と文字列を指定できます。使用したPythonとUnicodeデータの版も記録します。Python公式:unicodedata(新しいタブで開く)
import unicodedata
rows = [
("R01", "ABC-123"),
("R02", "ABC-123"),
("R03", "部品①"),
("R04", "部品1"),
("R05", "ガイド"),
("R06", "カ\u3099イト\u3099"),
("R07", "ガイド"),
("R08", "ABC\u2212123"),
("R09", "ABC-123 "),
("R10", "abc-123"),
]
result = [
{
"row_id": row_id,
"original": text,
"nfc": unicodedata.normalize("NFC", text),
"nfkc": unicodedata.normalize("NFKC", text),
}
for row_id, text in rows
]
print("Unicode data:", unicodedata.unidata_version)
for item in result:
print(item["row_id"], repr(item["original"]), repr(item["nfkc"]))
このコードは正規化結果を別の値として持ち、元の文字列を上書きしません。今回の10件はローカルで実行し、上の変換表と一致しました。出力を見て区別しにくい文字は、Unicodeのコードポイントも記録すると確認しやすくなります。
例えばR06の元の文字列には、U+3099が2か所含まれます。見た目だけで「同じ文字列だった」と判断せず、比較した値と使った形式を残します。フォントや画面での見え方を、このコードで検証したわけではありません。
同じ比較キーへ変わった行をまとめて確認する
今回のNFKC結果では、次の3グループが同じ値になります。
表は横にスクロールできます
| 比較キー | 元の行ID | この例から分かること |
|---|---|---|
| ABC-123 | R01、R02 | 全角とASCIIの差がなくなる |
| 部品1 | R03、R04 | 丸数字の区別がなくなる |
| ガイド | R05、R06、R07 | 半角表記や結合文字の差がまとまる |
元の文字列は10種類あり、NFKC後の異なる値は6種類です。ただし、行は10件のまま残します。比較キーが同じになる候補3グループ・7行を見つけたことと、4件を削除してよいことは同じではありません。
NFCでは、この例の変化はR06だけで、異なる値は9種類です。どちらの変換でも、結果が同じになった行を実世界の同じ品目・人物・会社と認定したことにはしません。
元の値と変換後の値、変換形式、変化の有無、同じ結果のグループを残して担当者へ渡します。正式名称を変更するのか、検索用の補助列だけに使うのかも分けて決めます。
AIへ、追加の置換を勝手に混ぜないよう指定する
文字列正規化の確認コードを作ってください。
環境:[言語・ライブラリ・バージョン]。
originalとrow_idを保持し、NFCとNFKCの結果を別列へ出してください。
空白除去、大小文字変換、似た記号の独自置換を追加しないでください。
変化した行と、変換後に同じ値になる行IDを示してください。
同じ結果になっても削除・統合せず、確認用の候補にしてください。
出力件数が入力件数と一致することを検査してください。
正式名称や同一人物かどうかは、この変換だけで確定しないでください。
見分けにくい文字は、元と変換後のコードポイントも示してください。
実行していなければ、実測した結果として説明しないでください。
入力:[必要な文字列と行ID]
空欄や欠損値を含む実データでは、文字列への変換規則も決めます。欠損値を「None」などの文字へ変えた結果が、実際のラベルと混ざることを避けます。今回のコードは10件すべてが文字列の例で、欠損処理まで実装した汎用クレンジングではありません。
空白除去や大小変換も必要なら、正規化とは別の段階として結果を記録し、その段階で新しく同じになる値を確認します。NFKCだけの検査が通ったことを、後から追加した変換の検査へ使い回さないでください。
正規化の完了と、台帳整理の完了を分ける
正規化は文字列の表現をそろえる処理であり、値の正しさや同一性を確認する仕組みではありません。識別子なら仕様上どの表記を許すか、名称なら原資料をどう保持するかへ戻ります。
CSVの削除規則と件数の管理は、CSVの重複行を整理する方法で扱います。顧客の同一性を判断する場合は、顧客台帳の表記ゆれ・重複整理へ進みます。今回の成果物は、変換で何が変わり、どの値が同じになったかを追える確認表です。
参考資料
公式仕様の確認日:2026年10月20日。ラベル10件は架空例で、Pythonコードをローカルで実行しました。AI生成、実台帳の同一性、正式名称の採用、他の実装環境、フォント・画面表示は検証していません。
