[Python] 結合文字を使用した濁点や半濁点を直前の仮名と結合させる方法(ウ゛→ ヴ)
まえがき
人名検索が行えるデータベースを作る際、提供されたデータにこんな読み仮名が混在していた。
- ヤマグチ
- ヤマク゛チ
ヤマグチと検索してもヤマク゛チさんがヒットしないので、「ク゛」を「グ」に寄せる形で正規化することにした。
適当なライブラリが見当たらなかったので、自前で実装することにした奮闘記。
実装方法
文字列内の合成文字を基底文字と結合文字へ分解
- 1で分解した文字列をバイト列に変換
- 2で変換したバイト列の濁点、半濁点をUnicode結合文字の濁点、半濁点に置換
- 3で置換されたバイト列を文字列型へ戻す
- (必要に応じて)合成文字に変換
Unicodeの合成文字と結合文字
日本語の「は」「ば」「ぱ」のように符号の有無・種類で音を表現する文字に対し、Unicodeでは合成文字と結合文字を使用する二通りの表現方法がある。
「ば」の例では以下の二種類がある。
-
ば
U+3070
-
は
+ 濁点U+306F
+U+3099
ば
は合成文字、は
は基底文字、濁点が結合文字である。
濁点゛
や半濁点゜
を結合文字へ置換してやればUnicode上、結合された1文字となる
Wikipediaにわかりやすい例があるので引用する。
例: â は U+00E2 (latin small letter a with circumflex) でも、U+0061 U+0302 (latin small letter a + combining circumflex accent) でも表すことができる。
結合文字 - Wikipedia
UTF-8の合成文字と結合文字
Unicode上での合成文字と結合文字の動作がわかったところで、Unicodeに対応した文字符号化方式で一番メジャーであろうUTF-8で合成文字と結合文字の動きを見る。
合成文字
import unicodedata
print(unicodedata.normalize("NFC", "ヤ マ グ チ").encode())
> b'\xe3\x83\xa4 \xe3\x83\x9e \xe3\x82\xb0 \xe3\x83\x81'
"ヤマグチ"のグが、\xe3\x82\xb0
で符号化されている
結合文字
import unicodedata
print(unicodedata.normalize("NFD", "ヤ マ グ チ").encode())
> b'\xe3\x83\xa4 \xe3\x83\x9e \xe3\x82\xaf\xe3\x82\x99 \xe3\x83\x81'
"ヤマグチ"のグが、\xe3\x82\xaf
と\xe3\x82\x99
の組み合わせで符号化されている
濁点・半濁点の文字符号
UnicodeとUTF-8でそれぞれ対応する符号を知る必要があるが、Wikipediaにあった。
濁点 - Wikipedia
半濁点 - Wikipedia
記号 | Unicode | UTF-8符号 | 備考 |
---|---|---|---|
゛ | U+309B | \xe3\x82\x9b | 全角濁点 |
- | U+3099 | \xe3\x82\x99 | 濁点(結合文字) |
゙ | U+FF9E | \xef\xbe\x9e | 半角濁点 |
゜ | U+309C | \xe3\x82\x9c | 全角半濁点 |
- | U+309A | \xe3\x82\x9a | 半濁点(結合文字) |
゚ | U+FF9F | \xef\xbe\x9f | 半角半濁点 |
全角濁点と半角濁点を濁点(結合文字)へ置換、
全角半濁点と半角半濁点を半濁点(結合文字)へ置換する
コード
#!/usr/bin/env python3
import re
import unicodedata
def join_diacritic(text, mode="NFC"):
"""
基底文字と濁点・半濁点を結合
"""
# str -> bytes
bytes_text = text.encode()
# 濁点Unicode結合文字置換
bytes_text = re.sub(b"\xe3\x82\x9b", b'\xe3\x82\x99', bytes_text)
bytes_text = re.sub(b"\xef\xbe\x9e", b'\xe3\x82\x99', bytes_text)
# 半濁点Unicode結合文字置換
bytes_text = re.sub(b"\xe3\x82\x9c", b'\xe3\x82\x9a', bytes_text)
bytes_text = re.sub(b"\xef\xbe\x9f", b'\xe3\x82\x9a', bytes_text)
# bytet -> str
text = bytes_text.decode()
# 正規化
text = unicodedata.normalize(mode, text)
return text
実行
join_diacritic("ルイズ・フランソワーズ・ル・ブラン・ド・ラ・ウ゛ァリエール")
> ルイズ・フランソワーズ・ル・ブラン・ド・ラ・ヴァリエール
応用
#!/usr/bin/env python3
import re
import unicodedata
def join_diacritic(text, mode="NFC"):
"""
基底文字と濁点・半濁点を結合
"""
# str -> bytes
bytes_text = text.encode()
# 濁点Unicode結合文字置換
bytes_text = re.sub(b"\xe3\x82\x9b", b'\xe3\x82\x99', bytes_text)
bytes_text = re.sub(b"\xef\xbe\x9e", b'\xe3\x82\x99', bytes_text)
# 半濁点Unicode結合文字置換
bytes_text = re.sub(b"\xe3\x82\x9c", b'\xe3\x82\x9a', bytes_text)
bytes_text = re.sub(b"\xef\xbe\x9f", b'\xe3\x82\x9a', bytes_text)
# bytet -> str
text = bytes_text.decode()
# 正規化
text = unicodedata.normalize(mode, text)
return text
join_diacritic("ルイズ・フランソワーズ・ル・ブラン・ド・ラ・ウ゛ァリエール")
> ルイズ・フランソワーズ・ル・ブラン・ド・ラ・ヴァリエール
ヴァリエールちゃんをウ゛ァリエールちゃんにしたい。
# 一度結合文字へ変換して
text = unicodedata.normalize("NFD", text)
# 結合文字を全角濁点・半濁点へ変換すればできる
bytes_text = re.sub(b'\xe3\x82\x99', b"\xe3\x82\x9b", bytes_text)
bytes_text = re.sub(b"\xe3\x82\x9a", b'\xe3\x82\x9c', bytes_text)
UTF-8以外の文字符号化方式に対応する
# 例えばこの様に実装にすれば、UTF-8以外にも対応できる
bytes_text = re.sub("\u309B".encode(charset), "\u3099".encode(charset), bytes_text)
bytes_text = re.sub("\uFF9E".encode(charset), "\u3099".encode(charset), bytes_text)
おわりに
Unicode結合文字の仕組み使うので、Shift_JISとかのお友達は一度Unicodeに準じた文字符号化方式にエンコードすることで使えるようになる(たぶん)
Author And Source
この問題について([Python] 結合文字を使用した濁点や半濁点を直前の仮名と結合させる方法(ウ゛→ ヴ)), 我々は、より多くの情報をここで見つけました https://qiita.com/syamamura/items/13c0825282415e2e360d著者帰属:元の著者の情報は、元のURLに含まれています。著作権は原作者に属する。
Content is automatically searched and collected through network algorithms . If there is a violation . Please contact us . We will adjust (correct author information ,or delete content ) as soon as possible .