ピンインの変換

日本人(だけじゃないかな?)が中国語学習をする上で一番大変なのは声調を覚えることというのは前にも書いたとおりです。そこで一生懸命覚えることになるのですが、例えば自分で電子的に単語帳を作る、それが本格的なDBだろうがExcelだろうが、ピンインの処理というのは意外に面倒です。

ピンインというのは母音の上に声調記号を乗せて表記します。こんな感じですよね。

yāng, píng, lǚ, lè

ただ、わざわざ声調記号付きのアルファベットを入力するために文字パレットを呼び出したり、ショートカットキーを覚えるのは面倒だと思います。なので、実際問題としてこんな感じで、ピンインの後ろに声調番号をつけてやるという対応をしている人が多いのかなと思っています。

yang1, ping2, lv3, le4

この番号を振る方式、いろいろ分析にかけたいと思うときは便利で、例えばPythonのスライスを使って最後の一文字を拾うだけで声調が分かります。

>>> 'yang1'[-1]
>>> 1

以前書いた声調の頻度を分析する話も、(もうちょっと手の込んだことをしていますが)基本的には、ピンインの最後の1文字で頻度分析をしてあげているだけです。ただ、内部的に保存したり解析したりする時にはそれで良いにしても、実際にピンインを表示させたいときには、元通りの正しい書式に戻したいという思うのではないでしょうか?

というか、そう思ったので、ちょっくらPythonで書いてみました。

基本ルールを押さえれば結構簡単だった

まずは、コンストラクタの方にself.tonesという属性を定義してあげます。これの中に、声調記号が載った場合の文字を入れておきます。

class Pinyin:
    def __init__(self):
        self.consonants = ('b', 'c', 'ch', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 'sh', 't',
                                          'w', 'x', 'y', 'z', 'ng')
        self.vowels = ('a', 'ai', 'ang', 'ao', 'e', 'ei', 'eng', 'er', 'i', 'ia', 'ian', 'iang', 'iao', 'ie', 'in', 'ing', 'iong',
                             'iu', 'o', 'ong', 'ou', 'u', 'ua', 'uai', 'uan', 'uang', 'ue', 'ui', 'un', 'uo', 'v', 've')
        self.tones = ( ( 'a', 'ā', 'á', 'ǎ', 'à'), ('o', 'ō', 'ó', 'ǒ', 'ò'), ('e', 'ē', 'é', 'ě', 'è'),
                               ('i', 'ī', 'í', 'ǐ', 'ì'), ('u', 'ū', 'ú', 'ǔ', 'ù'), ('ü', 'ǖ', 'ǘ', 'ǚ', 'ǜ'))

こちらが実際に変換を行うファンクションです。

    def pinyin_converter_d(self, pinyins):
        #Convert Pinyin from stored format (yang1) to display format (yāng)
        splitpin = self.pinyin_splitter(pinyins)
        if splitpin[0] == True:
            if 'a' in pinyins:
                convertkey = [0, int(splitpin[2][2])]
            elif 'o' in pinyins:
                convertkey = [1, int(splitpin[2][2])]
            elif 'e' in pinyins:
                convertkey = [2, int(splitpin[2][2])]
            elif 'ui' in pinyins:
                convertkey = [3, int(splitpin[2][2])]
            elif 'iu' in pinyins:
                convertkey = [4, int(splitpin[2][2])]
            elif 'i' in pinyins:
                convertkey = [3, int(splitpin[2][2])]
            elif 'u' in pinyins:
                convertkey = [4, int(splitpin[2][2])]
            elif 'v' in pinyins:
                convertkey = [5, int(splitpin[2][2])]

            #convert from v to ü
            if 'v' in pinyins:
                pinyins = re.sub('v', self.tones[5][0], pinyins)

            #remove tone number at the end
            if pinyins[-1].isdigit():
                pinyins = pinyins[:-1]

            return re.sub(self.tones[convertkey[0]][0], self.tones[convertkey[0]][convertkey[1]], pinyins)
        else:
            return ''

声調記号が載る母音にはルールがあって、"a -> o -> e -> i, u"という優先度になっています。つまり、1つのピンインの中に複数の母音がある場合にどの文字の上に記号を載せるかが決まっているわけです。多少面倒くさいのが、i と u で、この2つが並んだ場合は、後ろに来たモノに声調記号を載せることになっています。例えば、xiu4 というピンインなら xiù というように u の上に来て、gui4なら、今度は i の上になるので guì という感じですね。

もう1つ例外処理を考えないと行けないのが、v の扱いです。この文字は正式には ü と表記するルールになっていますが、通例 v で代用することが多いです。この文字の上に声調が来る場合は良いのですが、lve4 という表記をするピンインがあって、この場合は上のルールに従うと e の上に声調記号が来ます。ただ、声調記号の有無に関係なく、v は ü に変換しないと行けないのであらかじめロジックを仕込ませておいて、最終的には lüè となるのが正解です。

とりあえずソースは書けたので、いくつかのピンインで試してみましょう。

>>> ppp = Pinyin()
>>> print ppp.pinyin_converter_d('xiu4')
xiù
>>> print ppp.pinyin_converter_d('gui4')
guì
>>> print ppp.pinyin_converter_d('lve4')
lüè
>>> print ppp.pinyin_converter_d('lv3')
lǚ

大丈夫そうですね。これでデータベースを直接見ることがないユーザでも混乱させることなく表示することができます。