PHPの「mb_convert_kana」風　文字列置換え（JavaScript版）

# はじめに
「[今更ながら「mb_convert_kana」の変換する文字列の範囲について調べてみました](http://qiita.com/hrdaya/items/470b338e7c0014fe6dc7)」で「mb_convert_kana」がどのような変換を行うかを検証してみましたが、思っていたよりも変換の範囲が狭かったので、日本語の文字の揺らぎを丸める為の関数をPHP版とJavaScript版で作ってみました。

「[PHPの「mb_convert_kana」風　文字列置換え（PHP版）](http://qiita.com/hrdaya/items/e8e6297aee7d512ec131)」
「[PHPの「mb_convert_kana」風　文字列置換え（JavaScript版）](http://qiita.com/hrdaya/items/406a63d04b12adcd5572)」 :point_left: この投稿

# 参考
> * [Unicode一覧表](http://www.shurey.com/js/works/unicode.html)
* [Unicode](http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/index.html)
* [PHPのmb_convert_kanaとUnicodeのNFKC正規化](http://hydrocul.github.io/wiki/blog/2014/1127-unicode-nfkd-mb-convert-kana.html)
* [Unicodeにあるハイフン/マイナス/長音符/波線/チルダのコレクション](http://hydrocul.github.io/wiki/blog/2014/1101-hyphen-minus-wave-tilde.html)
* [日本語環境でのUnicodeの諸問題](http://www.wikiwand.com/ja/Unicode#/.E6.97.A5.E6.9C.AC.E8.AA.9E.E7.92.B0.E5.A2.83.E3.81.A7.E3.81.AEUnicode.E3.81.AE.E8.AB.B8.E5.95.8F.E9.A1.8C)
* [Unicodeに関連する問題](http://www.wikiwand.com/ja/%E6%B3%A2%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5#/Unicode.E3.81.AB.E9.96.A2.E9.80.A3.E3.81.99.E3.82.8B.E5.95.8F.E9.A1.8C)
* [アイヌ語](http://www.wikiwand.com/ja/%E3%82%A2%E3%82%A4%E3%83%8C%E8%AA%9E)
* [捨て仮名](http://www.wikiwand.com/ja/%E6%8D%A8%E3%81%A6%E4%BB%AE%E5%90%8D)
* [ヴ](http://www.wikiwand.com/ja/%E3%83%B4)

# convertKana()

## 使い方

「mb_convert_kana」と同じような使い方をします。
ただし、オプションに指定する文字列は先頭から順番に実行されるため、実行の順番によっては予想していた結果と異なる場合もあります。

説明はソースの中に入れていますので、詳しくはソースの中身を確認してください。

なお、簡単なテストコードは通していますが、組み合わせが多数あるため全てを網羅しているわけではありませんので、使用の際にはご自身でご検証ください。

```php
console.log(convertKana('ｱｲｳｴｵ１２３４５','c'));
// アイウエオ12345
```

## ソース

```js
/**
 *PHPの「mb_convert_kana」風置換え
 *
 * 入力文字列の揺らぎの修正を主な目的としていますので「mb_convert_kana」とは
 * 異なります。
 *
 * オプション文字列の先頭から変換関数を順番に実行していきます。
 *
 * 再変換時に元の文字列に戻る保障はありません。
 * 文字数が変わる可能性があります。
 * 「濁点」「半濁点」の揺らぎの修正をデフォルトで行います。
 * 「ゕゖ」を「ヵヶ」にデフォルトで変換します。
 * 「水平タブ(HT)」をスペース4文字に展開します。
 * 「改行(LF)」以外の制御文字を空文字に変換します。
 * 半角カタカナは全角カタカナに置き換えられます。
 *
 * オプションの相違点
 * 「h」「H」「K」「k」は存在しません。半角カタカナはデフォルトで全角カタカナに変換されます。
 * 「V」は「う濁」から「は濁」への変換となります。
 *
 * ひらがなに無いカタカナは変換しません。
 * 「ㇰ」「ㇱ」「ㇲ」「ㇳ」「ㇴ」「ㇵ」「ㇶ」「ㇷ」
 * 「ㇸ」「ㇹ」「ㇺ」「ㇻ」「ㇼ」「ㇽ」「ㇾ」「ㇿ」
 *
 * 合成できなかった濁点・半濁点は単独の濁点(U+309B)・半濁点(U+309C)になります。
 * NFKC正規化では「U+3099（゙）」「U+309A（゚）」ですがフォントによっては
 * うまく表示されないための対処です。
 * 「mb_convert_kana」と同じ処理になります。
 *
 * http://hydrocul.github.io/wiki/blog/2014/1127-unicode-nfkd-mb-convert-kana.html
 *
 * オプションで使用する文字列
 * r: 「全角」英字を「半角」に変換します。
 * R: 「半角」英字を「全角」に変換します。
 * n: 「全角」数字を「半角」に変換します。
 * N: 「半角」数字を「全角」に変換します。
 * a: 「全角」英数字記号を「半角」に変換します。
 * A: 「半角」英数字記号を「全角」に変換します。
 * s: 「全角」スペースを「半角」に変換します（U+3000 -> U+0020）。
 * S: 「半角」スペースを「全角」に変換します（U+0020 -> U+3000）。
 * c: 「全角カタカナ」を「全角ひらがな」に変換します。
 * C: 「全角ひらがな」を「全角カタカナ」に変換します。
 * v: 「う濁」を「は濁」に変換します。
 * V: 「ウ濁」を「ハ濁」に変換します。
 * Q: 「半角」クォーテーション、「半角」アポストロフィを「全角」に変換します。
 * q: 「全角」クォーテーション、「全角」アポストロフィを「半角」に変換します。
 * B: 「半角」バックスラッシュを「全角」に変換します。
 * b: 「全角」バックスラッシュを「半角」に変換します。
 * T: 「半角」チルダを「全角」にチルダ変換します。
 * t: 「全角」チルダを「半角」チルダに変換します。
 * W: 全角「波ダッシュ」を全角「チルダ」に変換します。
 * w: 全角「チルダ」を全角「波ダッシュ」に変換します。
 * P: 「ハイフン、ダッシュ、マイナス」を「全角ハイフンマイナス」に変換します。（U+FF0D）
 * p: 「ハイフン、ダッシュ、マイナス」を「半角ハイフンマイナス」に変換します。（U+002D）
 * U: 「U+0021」～「U+007E」以外の「半角」記号を「全角」記号に変換します。
 * u: 「U+0021」～「U+007E」以外の「全角」記号を「半角」記号に変換します。
 * X: 「カッコ付き文字」を「半角括弧と中の文字」に展開します。
 * Y: 集合文字を展開します。（単位文字以外）
 * Z: 小字形文字を大文字に変換します。（U+FE50～U+FE6B）
 *
 * @param {String} str 変換する文字列
 * @param {String} opt 変換オプション
 */
var convertKana = function (str, opt) {
    opt = opt === undefined ? '' : opt;
    // オプションが文字列でない場合はそのまま返す
    if (typeof str !== 'string' || typeof opt !== 'string') {
        return str;
    }

    // 文字の揺らぎ修正用の関数です。
    var init = function (_str) {
        // 「水平タブ(HT)」をスペース4文字に展開します。
        // 「改行(LF)」以外の制御文字を空文字に変換します。
        // 「U+3099（゙）」「U+309A（゚）」を単独の濁点
        // 「U+309B（゛）」「U+309C（゜）」に変換します。
        // 「ゕゖ」を「ヵヶ」に変換します。
        var regCtr = new RegExp(
                '[' +
                '\u0000-\u0008\u000B-\u001F' +
                '\u007F-\u009F' +
                '\u2000-\u200F' +
                '\u2028-\u202E' +
                '\u2060' +
                '\u206A-\u206F' +
                '\uFFF9-\uFFFB' +
                ']'
                , 'g');
        _str = _str.replace(/\u0009/g, '    ')
                .replace(regCtr, '').replace(/\u000A/g, '\n')
                .replace(/\u3099/g, '\u309B').replace(/\u309A/g, '\u309C')
                .replace(/ゕ/g, 'ヵ').replace(/ゖ/g, 'ヶ');


        // 半角カタカナを全角カタカナに変換します。
        // 全角・半角カタカナのマップ
        var kanaMap = {
            'ｶﾞ': 'ガ', 'ｷﾞ': 'ギ', 'ｸﾞ': 'グ', 'ｹﾞ': 'ゲ', 'ｺﾞ': 'ゴ',
            'ｻﾞ': 'ザ', 'ｼﾞ': 'ジ', 'ｽﾞ': 'ズ', 'ｾﾞ': 'ゼ', 'ｿﾞ': 'ゾ',
            'ﾀﾞ': 'ダ', 'ﾁﾞ': 'ヂ', 'ﾂﾞ': 'ヅ', 'ﾃﾞ': 'デ', 'ﾄﾞ': 'ド',
            'ﾊﾞ': 'バ', 'ﾋﾞ': 'ビ', 'ﾌﾞ': 'ブ', 'ﾍﾞ': 'ベ', 'ﾎﾞ': 'ボ',
            'ﾊﾟ': 'パ', 'ﾋﾟ': 'ピ', 'ﾌﾟ': 'プ', 'ﾍﾟ': 'ペ', 'ﾎﾟ': 'ポ',
            'ｳﾞ': 'ヴ', 'ﾜﾞ': 'ヴァ', 'ｦﾞ': 'ヴォ',
            'ｱ': 'ア', 'ｲ': 'イ', 'ｳ': 'ウ', 'ｴ': 'エ', 'ｵ': 'オ',
            'ｶ': 'カ', 'ｷ': 'キ', 'ｸ': 'ク', 'ｹ': 'ケ', 'ｺ': 'コ',
            'ｻ': 'サ', 'ｼ': 'シ', 'ｽ': 'ス', 'ｾ': 'セ', 'ｿ': 'ソ',
            'ﾀ': 'タ', 'ﾁ': 'チ', 'ﾂ': 'ツ', 'ﾃ': 'テ', 'ﾄ': 'ト',
            'ﾅ': 'ナ', 'ﾆ': 'ニ', 'ﾇ': 'ヌ', 'ﾈ': 'ネ', 'ﾉ': 'ノ',
            'ﾊ': 'ハ', 'ﾋ': 'ヒ', 'ﾌ': 'フ', 'ﾍ': 'ヘ', 'ﾎ': 'ホ',
            'ﾏ': 'マ', 'ﾐ': 'ミ', 'ﾑ': 'ム', 'ﾒ': 'メ', 'ﾓ': 'モ',
            'ﾔ': 'ヤ', 'ﾕ': 'ユ', 'ﾖ': 'ヨ',
            'ﾗ': 'ラ', 'ﾘ': 'リ', 'ﾙ': 'ル', 'ﾚ': 'レ', 'ﾛ': 'ロ',
            'ﾜ': 'ワ', 'ｦ': 'ヲ', 'ﾝ': 'ン',
            'ｧ': 'ァ', 'ｨ': 'ィ', 'ｩ': 'ゥ', 'ｪ': 'ェ', 'ｫ': 'ォ',
            'ｯ': 'ッ', 'ｬ': 'ャ', 'ｭ': 'ュ', 'ｮ': 'ョ',
            '｡': '。', '､': '、', 'ｰ': 'ー', '｢': '「', '｣': '」',
            '･': '・',
            'ﾞ': '゛', 'ﾟ': '゜'
        };
        var regHankana = new RegExp(
                '(' + Object.keys(kanaMap).join('|') + ')', 'g'
                );
        _str = _str.replace(regHankana, function (m) {
            return kanaMap[m];
        });

        // 濁点・半濁点付きの文字を一文字に変換します。
        //
        // 「ゔ」は「う゛」に展開されます。
        // 「わ゛」は「う゛ぁ」に変換されます。
        // 「ゐ゛」は「う゛ぃ」に変換されます。
        // 「ゑ゛」は「う゛ぇ」に変換されます。
        // 「を゛」は「う゛ぉ」に変換されます。
        // 「ヷ」「ワ゛」は「ヴァ」に展開されます。
        // 「ヸ」「ヰ゛」は「ヴィ」に展開されます。
        // 「ヹ」「ヱ゛」は「ヴェ」に展開されます。
        // 「ヺ」「ヲ゛」は「ヴォ」に展開されます。
        var dakutenMap = {
            'か゛': 'が', 'き゛': 'ぎ', 'く゛': 'ぐ', 'け゛': 'げ', 'こ゛': 'ご',
            'さ゛': 'ざ', 'し゛': 'じ', 'す゛': 'ず', 'せ゛': 'ぜ', 'そ゛': 'ぞ',
            'た゛': 'だ', 'ち゛': 'ぢ', 'つ゛': 'づ', 'て゛': 'で', 'と゛': 'ど',
            'は゛': 'ば', 'ひ゛': 'び', 'ふ゛': 'ぶ', 'へ゛': 'べ', 'ほ゛': 'ぼ',
            'は゜': 'ぱ', 'ひ゜': 'ぴ', 'ふ゜': 'ぷ', 'へ゜': 'ぺ', 'ほ゜': 'ぽ',
            'ゔ': 'う゛', 'ゝ゛': 'ゞ',
            'わ゛': 'う゛ぁ', 'ゐ゛': 'う゛ぃ', 'ゑ゛': 'う゛ぇ', 'を゛': 'う゛ぉ',
            'カ゛': 'ガ', 'キ゛': 'ギ', 'ク゛': 'グ', 'ケ゛': 'ゲ', 'コ゛': 'ゴ',
            'サ゛': 'ザ', 'シ゛': 'ジ', 'ス゛': 'ズ', 'セ゛': 'ゼ', 'ソ゛': 'ゾ',
            'タ゛': 'ダ', 'チ゛': 'ヂ', 'ツ゛': 'ヅ', 'テ゛': 'デ', 'ト゛': 'ド',
            'ハ゛': 'バ', 'ヒ゛': 'ビ', 'フ゛': 'ブ', 'ヘ゛': 'ベ', 'ホ゛': 'ボ',
            'ハ゜': 'パ', 'ヒ゜': 'ピ', 'フ゜': 'プ', 'ヘ゜': 'ペ', 'ホ゜': 'ポ',
            'ウ゛': 'ヴ', 'ヽ゛': 'ヾ',
            'ワ゛': 'ヴァ', 'ヰ゛': 'ヴィ', 'ヱ゛': 'ヴェ', 'ヲ゛': 'ヴォ',
            'ヷ': 'ヴァ', 'ヸ': 'ヴィ', 'ヹ': 'ヴェ', 'ヺ': 'ヴォ'
        };

        var reg = new RegExp('(' + Object.keys(dakutenMap).join('|') + ')', 'g');
        _str = _str.replace(reg, function (m) {
            return dakutenMap[m];
        });
        return _str;
    };

    // 文字列変換用の関数群です
    var convert = {
        // r: 「全角」英字を「半角」に変換します。
        r: function (_str) {
            return _str.replace(/[Ａ-Ｚａ-ｚ]/g, function (s) {
                return String.fromCharCode(s.charCodeAt(0) - 65248);
            });
        },
        // R: 「半角」英字を「全角」に変換します。
        R: function (_str) {
            return _str.replace(/[A-Za-z]/g, function (s) {
                return String.fromCharCode(s.charCodeAt(0) + 65248);
            });
        },
        // n: 「全角」数字を「半角」に変換します。
        n: function (_str) {
            return _str.replace(/[０-９]/g, function (s) {
                return String.fromCharCode(s.charCodeAt(0) - 65248);
            });
        },
        // N: 「半角」数字を「全角」に変換します。
        N: function (_str) {
            return _str.replace(/[0-9]/g, function (s) {
                return String.fromCharCode(s.charCodeAt(0) + 65248);
            });
        },
        // a: 「全角」英数字記号を「半角」に変換します。
        //
        // "a", "A" オプションに含まれる文字は、
        // U+0022, U+0027, U+005C, U+007Eを除く（" ' \ ~ ）
        // U+0021 - U+007E の範囲です。
        a: function (_str) {
            var reg = /[Ａ-Ｚａ-ｚ０-９！＃＄％＆（）＊＋，－．／：；＜＝＞？＠［］＾＿｀｛｜｝]/g;
            return _str.replace(reg, function (s) {
                return String.fromCharCode(s.charCodeAt(0) - 65248);
            });
        },
        // A: 「半角」英数字記号を「全角」に変換します 。
        //
        // "a", "A" オプションに含まれる文字は、
        // U+0022, U+0027, U+005C, U+007Eを除く（" ' \ ~ ）
        // U+0021 - U+007E の範囲です。
        A: function (_str) {
            var reg = /[A-Za-z0-9!#\$%&\(\)\*\+,\-\.\/:;<=>\?@\[\]\^_`\{\|\}]/g;
            return _str.replace(reg, function (s) {
                return String.fromCharCode(s.charCodeAt(0) + 65248);
            });
        },
        // s: 「全角」スペースを「半角」に変換します（U+3000 -> U+0020）。
        s: function (_str) {
            return _str.replace(/\u3000/g, '\u0020');
        },
        // S: 「半角」スペースを「全角」に変換します（U+0020 -> U+3000）。
        S: function (_str) {
            return _str.replace(/\u0020/g, '\u3000');
        },
        // c: 「全角カタカナ」を「全角ひらがな」に変換します。
        //
        // 「ヽヾ」は「ゝゞ」に変換されます。
        // 「ヴ」は「う゛」に展開されます。
        // 「ヶ」は変換されません。（変換先が「か」「が」「こ」の複数あるため）
        // 「ヵ」は「か」に変換されます。
        // http://www.wikiwand.com/ja/%E6%8D%A8%E3%81%A6%E4%BB%AE%E5%90%8D
        c: function (_str) {
            return _str
                    .replace(/[ァ-ン]/g, function (s) {
                        return String.fromCharCode(s.charCodeAt(0) - 0x60);
                    })
                    .replace(/ヴ/g, 'う゛')
                    .replace(/ヵ/g, 'か')
                    .replace(/ヽ/g, 'ゝ')
                    .replace(/ヾ/g, 'ゞ');
        },
        // C: 「全角ひらがな」を「全角カタカナ」に変換します。
        //
        // 「ゝゞ」は「ヽヾ」に変換されます。
        // 「う゛」は「ヴ」に結合されます。
        C: function (_str) {
            return _str
                    .replace(/[ぁ-ん]/g, function (s) {
                        return String.fromCharCode(s.charCodeAt(0) + 0x60);
                    })
                    .replace(/(ウ゛)/g, 'ヴ')
                    .replace(/ゝ/g, 'ヽ')
                    .replace(/ゞ/g, 'ヾ');
        },
        // v: 「う濁」を「は濁」に変換します。
        //
        // 「う゛ぁ」「う゛ぃ」「う゛」「う゛ぇ」「う゛ぉ」を
        // 「ば」「び」「ぶ」「べ」「ぼ」に変換します。
        v: function (_str) {
            return _str
                    .replace(/(う゛ぁ|ゔぁ)/g, 'ば')
                    .replace(/(う゛ぃ|ゔぃ)/g, 'び')
                    .replace(/(う゛ぇ|ゔぇ)/g, 'べ')
                    .replace(/(う゛ぉ|ゔぉ)/g, 'ぼ')
                    .replace(/(う゛|ゔ)/g, 'ぶ');
        },
        // V: 「ウ濁」を「ハ濁」に変換します。
        //
        // 「ヴァ」「ヴィ」「ヴ」「ヴェ」「ヴォ」を
        // 「バ」「ビ」「ブ」「ベ」「ボ」に変換します。
        V: function (_str) {
            return _str
                    .replace(/(ウ゛ァ|ヴァ)/g, 'バ')
                    .replace(/(ウ゛ィ|ヴィ)/g, 'ビ')
                    .replace(/(ウ゛ェ|ヴェ)/g, 'ベ')
                    .replace(/(ウ゛ォ|ヴォ)/g, 'ボ')
                    .replace(/(ウ゛|ヴ)/g, 'ブ');
        },
        // Q: 半角クォーテーション、半角アポストロフィを全角に変換します。
        Q: function (_str) {
            return _str.replace(/["']/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) + 65248);
            });
        },
        // q: 全角クォーテーション、全角アポストロフィを半角に変換します。
        q: function (_str) {
            return _str.replace(/[＂＇]/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) - 65248);
            });
        },
        // B: 半角バックスラッシュを全角に変換します。
        B: function (_str) {
            return _str.replace(/\\/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) + 65248);
            });
        },
        // b: 全角バックスラッシュを半角に変換します。
        b: function (_str) {
            return _str.replace(/＼/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) - 65248);
            });
        },
        // T: 半角チルダを全角にチルダ変換します。
        T: function (_str) {
            return _str.replace(/~/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) + 65248);
            });
        },
        // t: 全角チルダを半角チルダに変換します。
        t: function (_str) {
            return _str.replace(/～/g, function (m) {
                return String.fromCharCode(m.charCodeAt(0) - 65248);
            });
        },
        // W: 全角波ダッシュを全角チルダに変換します。
        W: function (_str) {
            return _str.replace(/〜/g, '～');
        },
        // w: 全角チルダを全角波ダッシュに変換します。
        w: function (_str) {
            return _str.replace(/～/g, '〜');
        },
        // P: ハイフン、ダッシュ、マイナスを全角ハイフンマイナスに変換します。（U+FF0D）
        //    英数記号の後ろにある全角・半角長音符も含む
        //
        // http://hydrocul.github.io/wiki/blog/2014/1101-hyphen-minus-wave-tilde.html
        //    「U+002D」半角ハイフンマイナス
        //    「U+FE63」小さいハイフンマイナス。NFKD/NFKC正規化で U+002D
        //    「U+FF0D」全角ハイフンマイナス
        //    「U+2212」「U+207B」「U+208B」マイナス
        //    「U+2010」「U+2011」ハイフン
        //    「U+2012」～「U+2015」「U+FE58」ダッシュ
        P: function (_str) {
            var reg = /[\u002D\uFE63\uFF0D\u2212\u207B\u208B\u2010-\u2015\uFE58]/g;
            var change = '\uFF0D';
            return _str.replace(reg, change)
                    .replace(/([\u0021-\u007E\uFF01-\uFF5E])(ー|ｰ)/g,
                            '$1' + change
                            );
        },
        // p: ハイフン、ダッシュ、マイナスを半角ハイフンマイナスに変換します。（U+002D）
        //    英数記号の後ろにある全角・半角長音符も含む
        //
        // http://hydrocul.github.io/wiki/blog/2014/1101-hyphen-minus-wave-tilde.html
        //    「U+002D」半角ハイフンマイナス
        //    「U+FE63」小さいハイフンマイナス。NFKD/NFKC正規化で U+002D
        //    「U+FF0D」全角ハイフンマイナス
        //    「U+2212」「U+207B」「U+208B」マイナス
        //    「U+2010」「U+2011」ハイフン
        //    「U+2012」～「U+2015」「U+FE58」ダッシュ
        p: function (_str) {
            var reg = /[\u002D\uFE63\uFF0D\u2212\u207B\u208B\u2010-\u2015\uFE58]/g;
            var change = '\u002D';
            return _str.replace(reg, change)
                    .replace(/([\u0021-\u007E\uFF01-\uFF5E])(ー|ｰ)/g,
                            '$1' + change
                            );
        },
        // U: 「U+0021」～「U+007E」以外の「半角」記号を「全角」記号に変換します。
        //
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/uff00.html
        U: function (_str) {
            var hanMap = {
                '⦅': '｟', '⦆': '｠', '¢': '￠', '£': '￡', '¬': '￢',
                '¯': '￣', '¦': '￤', '¥': '￥', '₩': '￦', '￨': '│',
                '￩': '←', '￪': '↑', '￫': '→', '￬': '↓', '￭': '■',
                '￮': '○'
            };

            var reg = new RegExp(
                    '(' + Object.keys(hanMap).join('|') + ')', 'g'
                    );
            return _str.replace(reg, function (m) {
                return hanMap[m];
            });
        },
        // u: 「U+0021」～「U+007E」以外の「全角」記号を「半角」記号に変換します。
        //
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/uff00.html
        u: function (_str) {
            var zenMap = {
                '｟': '⦅', '｠': '⦆', '￠': '¢', '￡': '£', '￢': '¬',
                '￣': '¯', '￤': '¦', '￥': '¥', '￦': '₩', '│': '￨',
                '←': '￩', '↑': '￪', '→': '￫', '↓': '￬', '■': '￭',
                '○': '￮'
            };

            var reg = new RegExp(
                    '(' + Object.keys(zenMap).join('|') + ')', 'g'
                    );
            return _str.replace(reg, function (m) {
                return zenMap[m];
            });
        },
        // X: カッコ付き文字を半角括弧と中の文字に展開します。
        //
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/u2460.html
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/u3200.html
        X: function (_str) {
            var kakomiMap = {
                '⑴': '(1)', '⑵': '(2)', '⑶': '(3)', '⑷': '(4)',
                '⑸': '(5)', '⑹': '(6)', '⑺': '(7)', '⑻': '(8)',
                '⑼': '(9)', '⑽': '(10)', '⑾': '(11)', '⑿': '(12)',
                '⒀': '(13)', '⒁': '(14)', '⒂': '(15)', '⒃': '(16)',
                '⒄': '(17)', '⒅': '(18)', '⒆': '(19)', '⒇': '(20)',
                '⒜': '(a)', '⒝': '(b)', '⒞': '(c)', '⒟': '(d)',
                '⒠': '(e)', '⒡': '(f)', '⒢': '(g)', '⒣': '(h)',
                '⒤': '(i)', '⒥': '(j)', '⒦': '(k)', '⒧': '(l)',
                '⒨': '(m)', '⒩': '(n)', '⒪': '(o)', '⒫': '(p)',
                '⒬': '(q)', '⒭': '(r)', '⒮': '(s)', '⒯': '(t)',
                '⒰': '(u)', '⒱': '(v)', '⒲': '(w)', '⒳': '(x)',
                '⒴': '(y)', '⒵': '(z)',
                '㈠': '(一)', '㈡': '(二)', '㈢': '(三)', '㈣': '(四)',
                '㈤': '(五)', '㈥': '(六)', '㈦': '(七)', '㈧': '(八)',
                '㈨': '(九)', '㈩': '(十)',
                '㈪': '(月)', '㈫': '(火)', '㈬': '(水)', '㈭': '(木)',
                '㈮': '(金)', '㈯': '(土)', '㈰': '(日)',
                '㈱': '(株)', '㈲': '(有)', '㈳': '(社)', '㈴': '(名)',
                '㈵': '(特)', '㈶': '(財)', '㈷': '(祝)', '㈸': '(労)',
                '㈹': '(代)', '㈺': '(呼)', '㈻': '(学)', '㈼': '(監)',
                '㈽': '(企)', '㈾': '(資)', '㈿': '(協)', '㉀': '(祭)',
                '㉁': '(休)', '㉂': '(自)', '㉃': '(至)'
            };

            var reg = new RegExp(
                    '(' + Object.keys(kakomiMap).join('|') + ')', 'g'
                    );
            return _str.replace(reg, function (m) {
                return kakomiMap[m];
            });
        },
        // Y: 集合文字を展開します。（単位文字以外）
        //
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/u2460.html
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/u3200.html
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/u3300.html
        Y: function (_str) {
            var syuugouMap = {
                '㌀': 'アパート', '㌁': 'アルファ', '㌂': 'アンペア', '㌃': 'アール',
                '㌄': 'イニング', '㌅': 'インチ', '㌆': 'ウォン', '㌇': 'エスクード',
                '㌈': 'エーカー', '㌉': 'オンス', '㌊': 'オーム', '㌋': 'カイリ',
                '㌌': 'カラット', '㌍': 'カロリー', '㌎': 'ガロン', '㌏': 'ガンマ',
                '㌐': 'ギガ', '㌑': 'ギニー', '㌒': 'キュリー', '㌓': 'ギルダー',
                '㌔': 'キロ', '㌕': 'キログラム', '㌖': 'キロメートル',
                '㌗': 'キロワット', '㌘': 'グラム', '㌙': 'グラムトン',
                '㌚': 'クルゼイロ', '㌛': 'クローネ', '㌜': 'ケース', '㌝': 'コルナ',
                '㌞': 'コーポ', '㌟': 'サイクル', '㌠': 'サンチーム',
                '㌡': 'シリング', '㌢': 'センチ', '㌣': 'セント', '㌤': 'ダース',
                '㌥': 'デシ', '㌦': 'ドル', '㌧': 'トン', '㌨': 'ナノ', '㌩': 'ノット',
                '㌪': 'ハイツ', '㌫': 'パーセント', '㌬': 'パーツ', '㌭': 'バーレル',
                '㌮': 'ピアストル', '㌯': 'ピクル', '㌰': 'ピコ', '㌱': 'ビル',
                '㌲': 'ファラッド', '㌳': 'フィート', '㌴': 'ブッシェル',
                '㌵': 'フラン', '㌶': 'ヘクタール', '㌷': 'ペソ', '㌸': 'ペニヒ',
                '㌹': 'ヘルツ', '㌺': 'ペンス', '㌻': 'ページ', '㌼': 'ベータ',
                '㌽': 'ポイント', '㌾': 'ボルト', '㌿': 'ホン', '㍀': 'ポンド',
                '㍁': 'ホール', '㍂': 'ホーン', '㍃': 'マイクロ', '㍄': 'マイル',
                '㍅': 'マッハ', '㍆': 'マルク', '㍇': 'マンション', '㍈': 'ミクロン',
                '㍉': 'ミリ', '㍊': 'ミリバール', '㍋': 'メガ', '㍌': 'メガトン',
                '㍍': 'メートル', '㍎': 'ヤード', '㍏': 'ヤール', '㍐': 'ユアン',
                '㍑': 'リットル', '㍒': 'リラ', '㍓': 'ルピー', '㍔': 'ルーブル',
                '㍕': 'レム', '㍖': 'レントゲン', '㍗': 'ワット',
                '㍿': '株式会社',
                '㍻': '平成', '㍼': '昭和', '㍽': '大正', '㍾': '明治',
                '㋀': '1月', '㋁': '2月', '㋂': '3月', '㋃': '4月',
                '㋄': '5月', '㋅': '6月', '㋆': '7月', '㋇': '8月',
                '㋈': '9月', '㋉': '10月', '㋊': '11月', '㋋': '12月',
                '㏠': '1日', '㏡': '2日', '㏢': '3日', '㏣': '4日',
                '㏤': '5日', '㏥': '6日', '㏦': '7日', '㏧': '8日',
                '㏨': '9日', '㏩': '10日', '㏪': '11日', '㏫': '12日',
                '㏬': '13日', '㏭': '14日', '㏮': '15日', '㏯': '16日',
                '㏰': '17日', '㏱': '18日', '㏲': '19日', '㏳': '20日',
                '㏴': '21日', '㏵': '22日', '㏶': '23日', '㏷': '24日',
                '㏸': '25日', '㏹': '26日', '㏺': '27日', '㏻': '28日',
                '㏼': '29日', '㏽': '30日', '㏾': '31日',
                '㍘': '0点', '㍙': '1点', '㍚': '2点', '㍛': '3点',
                '㍜': '4点', '㍝': '5点', '㍞': '6点', '㍟': '7点',
                '㍠': '8点', '㍡': '9点', '㍢': '10点', '㍣': '11点',
                '㍤': '12点', '㍥': '13点', '㍦': '14点', '㍧': '15点',
                '㍨': '16点', '㍩': '17点', '㍪': '18点', '㍫': '19点',
                '㍬': '20点', '㍭': '21点', '㍮': '22点', '㍯': '23点',
                '㍰': '24点',
                '⒈': '1.', '⒉': '2.', '⒊': '3.', '⒋': '4.',
                '⒌': '5.', '⒍': '6.', '⒎': '7.', '⒏': '8.',
                '⒐': '9.', '⒑': '10.', '⒒': '11.', '⒓': '12.',
                '⒔': '13.', '⒕': '14.', '⒖': '15.', '⒗': '16.',
                '⒘': '17.', '⒙': '18.', '⒚': '19.', '⒛': '20.',
                '№': 'No.', '℡': 'TEL', '㏍': 'K.K.', '㏇': 'Co.',
                '㏂': 'a.m.', '㏘': 'p.m.'
            };

            var reg = new RegExp(
                    '(' + Object.keys(syuugouMap).join('|') + ')', 'g'
                    );
            return _str.replace(reg, function (m) {
                return syuugouMap[m];
            });
        },
        // Z: 小字形文字を大文字に変換します。（U+FE50～U+FE6B）
        // 「﹐﹑﹒﹔﹕﹖﹗﹘﹙﹚﹛﹜﹝﹞﹟﹠﹡﹢﹣﹤﹥﹦﹨﹩﹪﹫」
        //
        // 「U+FF58」は「U+2014」へマッピングされていますが、揺らぎの訂正のため
        // 「U+002D（半角ハイフンマイナス）」に変換します。
        //
        // http://www.asahi-net.or.jp/~ax2s-kmtn/ref/unicode/ufe50.html
        Z: function (_str) {
            var komojiMap = {
                '\uFE50': '\u002C', '\uFE51': '\u3001',
                '\uFE52': '\u002E', '\uFE54': '\u003B',
                '\uFE55': '\u003A', '\uFE56': '\u003F',
                '\uFE57': '\u0021', '\uFE58': '\u002D',
                '\uFE59': '\u0028', '\uFE5A': '\u0029',
                '\uFE5B': '\u007B', '\uFE5C': '\u007D',
                '\uFE5D': '\u3014', '\uFE5E': '\u3015',
                '\uFE5F': '\u0023', '\uFE60': '\u0026',
                '\uFE61': '\u002A', '\uFE62': '\u002B',
                '\uFE63': '\u002D', '\uFE64': '\u003C',
                '\uFE65': '\u003E', '\uFE66': '\u003D',
                '\uFE68': '\\', '\uFE69': '\u0024',
                '\uFE6A': '\u0025', '\uFE6B': '\u0040'
            };

            var reg = new RegExp(
                    '(' + Object.keys(komojiMap).join('|') + ')', 'g'
                    );
            return _str.replace(reg, function (m) {
                return komojiMap[m];
            });
        }
    };

    // 文字列の初期化（揺らぎの訂正）を行ないます。
    str = init(str);

    // オプション文字列を分解して一文字ごとに応じた関数を実行します。
    opt.split('').map(function (o) {
        if (convert.hasOwnProperty(o)) {
            str = convert[o](str);
        }
    });

    return str;
}
```