全角・半角・ハイフンを整える住所正規化の基本
日本の住所の表記ゆれを安全に直す方法。NFKC の副作用、全角・半角とハイフンの判別、長音符を壊さない条件まで、コード付き。
執筆:Bruce Lee
同じ住所でも、3-1-13 と 3-1-13 と 3ー1ー13 は別の文字列です。正規化せずに単純な文字列として突合すると一致しません。検索や外部APIが表記ゆれを吸収するかは実装によって異なるため、入力側で安全な範囲を揃えておくと扱いやすくなります。
ここでは安全に修正できる範囲だけを整える方法を説明します。機械的に判断できない部分は修正せず、利用者に伝えるという方針です。
動作環境
- Node.js 24.x / ブラウザ(
String.prototype.normalizeがあれば動きます) - TypeScript 5.9(strict)
このページのコードは外部通信をしません。ブラウザ内だけで完結します。
1. NFKC でまとめて正規化する
Unicode の互換正規化(NFKC)は、全角数字・半角カナ・合成済み記号を「標準的な形」に揃えます。まず、最初にNFKCを適用します。
'東京都新宿区西新宿2-8-1'.normalize('NFKC')
// → '東京都新宿区西新宿2-8-1'NFKC で変換できるもの:
| 入力 | 出力 | 種類 |
|---|---|---|
123 | 123 | 全角数字 |
ABC | ABC | 全角英字 |
(U+3000) | | 全角空白 |
ガーデン | ガーデン | 半角カナ |
-(U+FF0D) | - | 全角ハイフンマイナス |
2. 末尾で NFC に戻す
NFKC の途中結果には、分解された形が残ることがあります。ガ が「カ + 濁点」の2文字になっていると、見た目は同じでも === で一致しません。
処理の最後に必ず合成形へ戻します。
const normalized = input
.normalize('NFKC') /* …他の処理… */
.normalize('NFC')テストで担保しておくと安全です。
expect(normalized).toBe(normalized.normalize('NFC'))3. ハイフンを統一する — ただし長音符に注意
住所に混ざるハイフンのような文字は1種類ではありません。NFKC で全角ハイフンは変換できますが、それ以外は個別に統一する必要があります。
// 常にハイフンとして扱ってよいもの
const ALWAYS_HYPHEN = /[‐‑‒–—―⁃−﹘﹣]/g
// 目に見えないソフトハイフンは削除する
const SOFT_HYPHEN = //g問題は U+30FC(長音符 ー) です。
'3ー1ー13' // これはハイフンのつもり
'スカイタワー' // これは日本語の一部同じ文字なので、無条件に置換すると スカイタワ- になります。建物名が壊れます。
数字に挟まれているときだけハイフンとして扱うのが安全な線引きです。
const PROLONGED_BETWEEN_DIGITS = /(?<=[0-9])ー(?=[0-9])/g
export function unifyHyphens(text: string): string {
return text
.replace(SOFT_HYPHEN, '')
.replace(ALWAYS_HYPHEN, '-')
.replace(PROLONGED_BETWEEN_DIGITS, '-')
}unifyHyphens('新宿3ー1ー13 コーポ東京')
// → '新宿3-1-13 コーポ東京' ← 番地だけ変換、建物名はそのまま後読み(lookbehind、(?<=)は Safari 16.4 以降で使えます。それより古い環境を対象にする場合は、replace のコールバックで前後の文字を確認する実装に置き換えてください。
4. 郵便番号を 123-4567 に揃える
〒、区切りのない7桁、空白区切り、全角ハイフン — どれも同じ郵便番号です。
// NFKC とハイフン統一のあとに実行する前提
const POSTAL_PATTERN = /〒?\s*(?<![0-9])([0-9]{3})[\s-]?([0-9]{4})(?![0-9])/
export function findPostalCode(text: string): string | null {
const match = POSTAL_PATTERN.exec(text)
if (!match) return null
return `${match[1]}-${match[2]}`
}(?<![0-9]) と (?![0-9]) を付けているのは、長い数字の途中から7桁を切り出さないためです。これがないと 第12345678ビル から 123-4567 を拾ってしまいます。
5. 空白を整える
NFKC で全角空白は半角になっているため、あとは連続する空白を1つにまとめます。
text.replace(/[ \t\r\n]+/g, ' ').trim()空白を全部削ってはいけません。 「西新宿2-8-1 テストビル」の空白は、住所と建物名の区切りとして意味を持っていることがあります。
6. 直してはいけないもの
次のものは修正せず、問題の可能性を伝えるだけにしてください。
都道府県の補完 「大阪市北区…」から「大阪府」を補いたくなりますが、自動では補完しないでください。同名の自治体がある場合や、入力者が意図的に省略している場合があります。都道府県がないことを伝え、必要に応じて入力してもらいます。
丁目・番・号の書き換え
2丁目8番1号 と 2-8-1 は相互に変換できそうに見えますが、元の表記に意味がある場合があります。 登記や公的書類の写しを扱うなら特にそうです。
異体字・旧字
髙(はしごだか)を 高 に、﨑(たつさき)を 崎 に変換しないでください。氏名や地名では別の文字として扱われます。ただし外部サービスが扱えないことがあるので、含まれていることは伝えてください。
建物名の切り出し 「どこまでが住所で、どこからが建物名か」は、表記だけでは判断できません。自動で切り分けず、「建物名が含まれている可能性があります」と伝えるにとどめます。
7. 変更点を必ず見せる
正規化の出力は「形式の候補」であって、正解ではありません。何をどう変えたかを1件ずつ出してください。
type Change = {
rule: string
before: string
after: string
reason: string // 日本語の説明をそのまま表示する
}8. 正規化で解決しないこと
正規化しても住所が実在するとは限りません。 「東京都新宿区西新宿9-99-99」はきれいに整いますが、存在するかは別の話です。実在性を判定したい場合は住所検証APIの比較を参照してください。
郵便番号から住所を引くのは正規化ではありません。 それは検索です。郵便番号検索APIの比較が該当します。
関連ページ
- 住所検証・住所正規化・ジオコーディングの違い — 用語の整理
- 日本の住所入力フォーム設計 — そもそも汚れた入力を減らす
- 住所整形ツール — この記事の処理を実際に動かす
出典
- MDN: String.prototype.normalize()(新しいタブで開く)確認日 2026-07-29
- Unicode Standard Annex #15: Unicode Normalization Forms(新しいタブで開く)確認日 2026-07-29