旧字体の対応方法

常用漢字じゃない旧字体とかが「・」になっちゃう原因

「・」に化けてしまう原因は 文字コード(エンコーディング)の不一致 です。旧字体や異体字などが含まれるテキストを正しく扱うには、UTF-8UTF-16 など、該当文字をサポートするエンコーディングを明示的に指定すれば解消できます。

Windowsではデフォルト文字コードが cp932(Shift_JISの派生)であり、これが旧字体をサポートしていません。
そのため、読み込み時に未知の文字が「・」や「?」に置き換わります。

対処方法:UTF-8で明示的に読み書きする

ファイル読み込み例

with open("input.txt", "r", encoding="utf-8") as f:
    text = f.read()
print(text)

ファイル書き込み例

with open("output.txt", "w", encoding="utf-8") as f:
    f.write(text)

重要ポイント

  • 旧字体が正しく表示されるには、ファイル自体がUTF-8で保存されている必要 があります。
  • もし既存ファイルが cp932 で保存されている場合は、テキストエディタ(例:VSCodeやメモ帳)でUTF-8に再保存してください。

特定文字をUnicodeコードポイントで置換する方法

旧字体を明示的に置換したい場合、Unicodeエスケープ(\uXXXX\U000XXXXX)を使えます。

text = "舊字體"  # 旧字体を含む例

# 旧字体「舊」を新字体「旧」に置換
text = text.replace("\u820a", "旧")

print(text)

※ここで \u820a は「舊」のUnicodeコードポイントです。

フォントも注意

コンソールやフォントがその文字をサポートしていない場合、文字化けや「□」「?」表示になることもあります。
正しく出すには、Windows TerminalやVSCodeのターミナルなどUTF-8対応かつCJKフォント対応の環境を使う必要があります。

\u820aみたいなユニコードをPythonで調べる方法

■ 文字からUnicodeコードポイントを調べる

char = "舊"
print(hex(ord(char)))   # 16進表記
print(ord(char))        # 10進表記

出力例:

0x820a
33290

→ 「舊」は Unicode コードポイント U+820A

コードポイントから文字を出す

print("\u820a")   # 舊
print(chr(0x820a))

どちらも同じ結果:「舊」

文字名を調べる

Pythonのunicodedataモジュールを使うと、文字の正式名称も取得できます。

import unicodedata

char = "舊"
print(unicodedata.name(char))

出力:

CJK UNIFIED IDEOGRAPH-820A

このように「舊」はCJK統合漢字の一つだとわかります。

逆に、Unicode名からコードポイントや文字を取得

import unicodedata

char = unicodedata.lookup("CJK UNIFIED IDEOGRAPH-820A")
print(char)           # 舊
print(hex(ord(char))) # 0x820a

外部サイトで調べる方法(補助的)

ブラウザで検索:

U+820A unicode

代表的なサイト:

これらではフォントの違いや対応環境も確認できます。

ユニコード調査方法まとめ

目的Pythonコード結果例
文字→Unicodeコードhex(ord('舊'))'0x820a'
Unicodeコード→文字chr(0x820a)'舊'
Unicode名を調べるunicodedata.name('舊')'CJK UNIFIED IDEOGRAPH-820A'
Unicode名→文字unicodedata.lookup('CJK UNIFIED IDEOGRAPH-820A')'舊'

Pandasでひとつの列に旧字体があるかどうかを判定する方法(5万行くらい想定)

方法①:Unicodeブロック+常用漢字リストで判定(おすすめ)

Pythonには「これは漢字か」「記号か」などを簡単に判断する標準機能はありません。
ただし、常用漢字リストを用意して、文字ごとに比較すれば実用的に判定できます。

例コード

import pandas as pd

# 仮のデータフレーム例
df = pd.DataFrame({
    'text': ['学校', '舊字体', '東京', '舊家', '京都']
})

# 常用漢字リストを外部ファイルや文字列で用意(例:文科省の常用漢字表など)
# ここでは簡略的に例として少数のみ定義
joyo_kanji = set("一二三四五六七八九十学校東京京都家体字")  # 実際は2136字以上

# 各行について旧字体を含むか判定
def has_kyujitai(s):
    for ch in s:
        if '\u4e00' <= ch <= '\u9fff':  # 漢字っぽい範囲
            if ch not in joyo_kanji:     # 常用漢字にない
                return True
    return False

df['has_kyujitai'] = df['text'].apply(has_kyujitai)

print(df)

出力例:

    text  has_kyujitai
0    学校         False
1   舊字体          True
2    東京         False
3    舊家          True
4    京都         False

方法②:旧字体のUnicodeコード範囲で推定する(粗め)

旧字体は多くがCJK統合漢字 (U+4E00U+9FFF) に含まれますが、それ自体では「旧字体かどうか」は区別できません。

一部はCJK拡張A・B・Cなど(U+3400U+2EBE0)に存在するため、それらを含む場合は「旧字体や異体字の可能性あり」として検出できます。

def has_extended_kanji(s):
    for ch in s:
        code = ord(ch)
        if (0x3400 <= code <= 0x4DBF) or (0x20000 <= code <= 0x2EBEF):
            return True
    return False

df['has_extended'] = df['text'].apply(has_extended_kanji)

この方法は「拡張漢字=旧字体かも」と推定する簡易判定です。

方法③:正確にやりたい場合(IPA辞書などを利用)

もし精度を上げたいなら、

  • 常用漢字リスト(文科省の公式リスト)
  • 異体字辞典(IPA辞書・Unihanデータベースなど)を使って突き合わせることもできます。

たとえば、IPA辞書に登録されていない漢字を「旧字体候補」とみなすことも可能ですが、これは大規模辞書照合になるため、5万行でも十分高速に動かすには前処理(集合化)が重要です。

方法④:ベクトル化して高速化(必要に応じて)

5万行程度であれば、上記の .apply() でも十分高速(数秒以内)です。
ただし100万行を超えるようなら、NumPyベクトル化やCython化を検討できます。

判定方法まとめ

方法特徴精度実用性
常用漢字リスト比較(方法①)シンプルで確実
Unicode拡張範囲検出(方法②)手軽で高速
辞書突合(方法③)高精度非常に高△(準備が必要)

置換して修正したい場合

Python(特にPandas)では、Unicode辞書を使って旧字体→新字体のマッピング変換を行うのが最も実用的で安全です。

【基本アイデア】

str.translate() または re.sub() と辞書を組み合わせて置換する。

方法①:str.translate()+辞書(最も高速)

Python標準の str.translate() は、Unicodeコードポイント(数値)をキーとする辞書を使って超高速に一括置換します。
旧字体の数が多くても(数百字程度なら)問題ありません。

例コード

import pandas as pd

# 例:旧字体を含むデータ
df = pd.DataFrame({
    'text': ['學校', '舊家', '東京', '體育館', '德川']
})

# 旧字体→新字体の対応表(例)
kanji_map = {
    ord('學'): '学',
    ord('舊'): '旧',
    ord('體'): '体',
    ord('德'): '徳'
}

# 一括変換(新列に修正結果を入れる例)
df['normalized'] = df['text'].str.translate(kanji_map)

print(df)

出力例

    text normalized
0    學校        学校
1    舊家        旧家
2    東京        東京
3   體育館       体育館
4    德川        徳川

ポイント

  • ord('舊')'舊' のUnicodeコードポイント(数値)
  • .str.translate() はC実装なので、5万行程度でも非常に速い(数百ms〜1秒未満)

方法②:通常のPython辞書と文字列置換

もし変換対象が少なく、可読性を優先するならこちらでもOK。

replace_dict = {
    '舊': '旧',
    '學': '学',
    '體': '体',
    '德': '徳'
}

def replace_kyujitai(s):
    for old, new in replace_dict.items():
        s = s.replace(old, new)
    return s

df['normalized'] = df['text'].apply(replace_kyujitai)

この方法でも5万行程度なら十分速いですが、translate()よりやや遅いです(2〜3倍くらい)。

方法③:regexを使う(特殊ケース向け)

置換対象が複雑(例:異体字を正規表現パターンで検出)なら re.sub() も使えますが、
単純な1対1置換にはtranslate()が圧倒的に効率的です。

置換方法まとめ

方法特徴処理速度推奨度
str.translate()ord()辞書高速・簡潔・安全◎(ベスト)★★★
.replace()ループわかりやすい・柔軟★★
re.sub()正規表現対応・複雑置換可

補足:辞書をJSONなどで管理する場合

辞書をJSONファイルで管理してもOKです。

import json

# JSONファイル読み込み
with open("kyujitai_map.json", "r", encoding="utf-8") as f:
    mapping = json.load(f)

# 文字→文字の辞書を ord() に変換
trans_map = {ord(k): v for k, v in mapping.items()}

# 一括置換
df['normalized'] = df['text'].str.translate(trans_map)

このようにすれば、変換ルールを外部管理しつつ簡単に更新できます。

旧字体→新字体のマッピング辞書の管理

旧字体→新字体のマッピング辞書を長期的に管理・拡張していくなら、
「人間が編集しやすく」かつ「Pythonで読み込みやすい」形式を選ぶことが重要です。

最もおすすめは CSV または JSON(UTF-8)形式。用途に応じて次のように使い分けるのが現実的です。

形式特徴向いている用途
CSV(UTF-8)Excelで編集しやすく、シンプル人手で追加・修正が多い場合
JSONPythonでそのまま辞書化できる自動処理・スクリプト連携が多い場合
Excel(.xlsx)フォントで確認しやすい、社内共有に向く目視でレビュー・複数人管理する場合
SQLite / Parquet大規模・システム連携用頻繁に更新+検索が必要な場合

CSV形式(最もバランス良い)

編集も楽で、Pandasで簡単に読み書き可能。

例:kyujitai_map.csv

old,new
舊,旧
體,体
學,学
德,徳

読み込みと辞書化

import pandas as pd

df_map = pd.read_csv("kyujitai_map.csv", encoding="utf-8")
kyujitai_dict = dict(zip(df_map['old'], df_map['new']))
trans_map = {ord(k): v for k, v in kyujitai_dict.items()}

追加運用

新しい旧字体を見つけたらExcelなどで開いて追記し、上書き保存するだけ。
文字化け防止のため、UTF-8で保存が必須です。

JSON形式(スクリプト運用中心ならおすすめ)

人間にも読みやすく、Pythonでそのまま辞書として使えます。

例:kyujitai_map.json

{
    "舊": "旧",
    "體": "体",
    "學": "学",
    "德": "徳"
}

読み込み

import json

with open("kyujitai_map.json", "r", encoding="utf-8") as f:
    kyujitai_dict = json.load(f)

trans_map = {ord(k): v for k, v in kyujitai_dict.items()}

追加

新しい対応を見つけたら以下のように更新できます。

kyujitai_dict["壽"] = "寿"

with open("kyujitai_map.json", "w", encoding="utf-8") as f:
    json.dump(kyujitai_dict, f, ensure_ascii=False, indent=2)

Excel形式(.xlsx)

見た目で確認したい場合や、他の人が非プログラマである場合に便利。

旧字体新字体

読み込みも簡単です。

df_map = pd.read_excel("kyujitai_map.xlsx")
kyujitai_dict = dict(zip(df_map['旧字体'], df_map['新字体']))

注意点

  • 保存時に文字化けしないよう、フォント設定を適切に。
  • Excelは手入力時に全角スペースや見た目の類似文字が混ざることがあるので注意。

SQLiteデータベース(将来的に拡張予定がある場合)

大規模・履歴管理やWebアプリ連携を想定するなら、
SQLiteで「旧字体テーブル」を作るのも堅牢です。

CREATE TABLE kyujitai_map (
  old TEXT PRIMARY KEY,
  new TEXT NOT NULL,
  note TEXT
);

Pythonで更新:

import sqlite3
conn = sqlite3.connect("kyujitai.db")
conn.execute("INSERT OR REPLACE INTO kyujitai_map VALUES (?, ?, ?)", ("舊", "旧", "手動追加"))
conn.commit()

おすすめ運用パターン(実践向け)

  1. メイン管理はCSVまたはJSON(UTF-8)
  2. 定期的にPandasで読み込んで .str.translate() に適用
  3. 新しい旧字体を発見したら
    • 一時的にPython辞書に追加してテスト
    • 問題なければCSV/JSONに追記
  4. 変更履歴をGitなどで管理(差分追跡が容易)

管理方法まとめ

目的推奨形式理由
手動で追加・Excel確認したいCSV(UTF-8)Excelで直接編集可能
自動処理やスクリプト連携中心JSONそのまま辞書化・高速
チーム共有・レビュー重視Excel(.xlsx)見やすく共有しやすい
大規模・履歴・検索が必要SQLiteスケーラブルで堅牢

実務的おすすめ構成:

  • 運用ファイル:kyujitai_map.csv(UTF-8)
  • 読み込み時に ord() 変換して .str.translate() で一括置換
  • 更新時はCSVに追記(Git管理)

これが最も長期的・堅牢・可搬性の高い方法です。

-Pandas, Python