常用漢字じゃない旧字体とかが「・」になっちゃう原因
「・」に化けてしまう原因は 文字コード(エンコーディング)の不一致 です。旧字体や異体字などが含まれるテキストを正しく扱うには、UTF-8 や UTF-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+4E00〜U+9FFF) に含まれますが、それ自体では「旧字体かどうか」は区別できません。
一部はCJK拡張A・B・Cなど(U+3400〜U+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で編集しやすく、シンプル | 人手で追加・修正が多い場合 |
| JSON | Pythonでそのまま辞書化できる | 自動処理・スクリプト連携が多い場合 |
| 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()
おすすめ運用パターン(実践向け)
- メイン管理はCSVまたはJSON(UTF-8)
- 定期的にPandasで読み込んで
.str.translate()に適用 - 新しい旧字体を発見したら
- 一時的にPython辞書に追加してテスト
- 問題なければCSV/JSONに追記
- 変更履歴をGitなどで管理(差分追跡が容易)
管理方法まとめ
| 目的 | 推奨形式 | 理由 |
|---|---|---|
| 手動で追加・Excel確認したい | CSV(UTF-8) | Excelで直接編集可能 |
| 自動処理やスクリプト連携中心 | JSON | そのまま辞書化・高速 |
| チーム共有・レビュー重視 | Excel(.xlsx) | 見やすく共有しやすい |
| 大規模・履歴・検索が必要 | SQLite | スケーラブルで堅牢 |
実務的おすすめ構成:
- 運用ファイル:
kyujitai_map.csv(UTF-8) - 読み込み時に
ord()変換して.str.translate()で一括置換 - 更新時はCSVに追記(Git管理)
これが最も長期的・堅牢・可搬性の高い方法です。