1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ひらがな⇔カタカナ変換は「0x60を足し引きするだけ」——Unicodeの並びと関数合成で組むかな変換

1
Posted at

「カタカナのフリガナをひらがなに統一したい」「半角カナで届いたデータを読みやすく直したい」——日本語データを扱うとかな変換は地味に頻出する。実装してみると、ひらがなとカタカナの相互変換は拍子抜けするほど簡単で、面倒なのは半角カナが絡んだときだけ、という構造が見えてくる。

ひらがな・カタカナ・半角カナを相互変換するツールをぱんだツールズの1機能として作った。ライブラリなし・ブラウザ完結。

この記事では、ひらがな⇔カタカナが Unicode 上でなぜ 0x60 の足し引きだけで変換できるのか、そして「半角カナ→ひらがな」のような多段変換を関数合成でどう素直に組むかを解説する。

ひらがな ⇔ カタカナ:0x60 を足し引きするだけ

Unicode では、ひらがな(U+3041U+3096)とカタカナ(U+30A1U+30F6)がまったく同じ並び順で配置されている。 がそれぞれ対応する位置にいて、その差はちょうど 0x60(10進で96)。

ひらがな 'あ' = U+3042      カタカナ 'ア' = U+30A2      差 = 0x60
ひらがな 'ん' = U+3093      カタカナ 'ン' = U+30F3      差 = 0x60

だから、対象の文字コード範囲に正規表現でヒットさせて、コードポイントを 0x60 足し引きするだけで変換できる。マッピングテーブルは要らない。

// ひらがな → カタカナ(U+3041-U+3096)
function hiraganaToKatakana(text: string): string {
  return text.replace(/[ぁ-ゖ]/g, (c) =>
    String.fromCharCode(c.charCodeAt(0) + 0x60))
}

// カタカナ → ひらがな(U+30A1-U+30F6)
function katakanaToHiragana(text: string): string {
  return text.replace(/[ァ-ヶ]/g, (c) =>
    String.fromCharCode(c.charCodeAt(0) - 0x60))
}

replace のコールバックで1文字ずつコードをずらすだけ。範囲外の文字(漢字・英数字・記号)は正規表現にマッチしないのでそのまま残る。「文章の一部のカナだけ変換」が自然にできる。

濁音・半濁音も気にしなくていい。U+304C)と U+30AC)も同じ 0x60 差で並んでいるので、合成済みの濁音文字がそのまま1対1で変換される。範囲を U+3096)までにしているのは、それ以降の ゛゜ゝゞ(繰り返し記号など)はカタカナ側に対応がなく、ずらすと変な文字になるため。

ここまでが「拍子抜けするほど簡単」な部分。

半角カナだけは別世界(マッピングテーブルが要る)

簡単なのはここまで。半角カタカナ( = U+FF71 など)は、全角カナとは並び順もオフセットも揃っておらず、0x60 のような一律の足し引きが効かない。素直にマッピングテーブルを持つ。

const HANKAKU_TO_ZENKAKU: Record<string, string> = {
  '': '', '': '', '': '', /* ... */
  '': '', '': '', '': '', '': '',
}

さらに半角カナは濁音を持たない。 は半角だと (カ)+ (濁点)の2文字で表現される。なので半角→全角では、次の文字が濁点・半濁点かを先読みして、濁音テーブルで1文字に合成する。

const DAKUTEN_MAP: Record<string, string> = {
  '': '', '': '', /* ... */ '': '',   // ヴ → ヴ も拾う
}
const HANDAKUTEN_MAP: Record<string, string> = {
  '': '', '': '', '': '', '': '', '': '',
}

function hankakuToZenkakuKana(text: string): string {
  let result = ''
  for (let i = 0; i < text.length; i++) {
    const c = text[i]
    const next = text[i + 1]
    const zenkaku = HANKAKU_TO_ZENKAKU[c]
    if (!zenkaku) { result += c; continue }
    if (next === '') {
      result += DAKUTEN_MAP[zenkaku] ?? zenkaku + ''   // 濁音化、無ければ濁点を添える
      i++
    } else if (next === '') {
      result += HANDAKUTEN_MAP[zenkaku] ?? zenkaku + ''
      i++
    } else {
      result += zenkaku
    }
  }
  return result
}

DAKUTEN_MAP[zenkaku] ?? zenkaku + '゛' のフォールバックがポイント。ア゙(濁点の付かないアに濁点)のような無効な組み合わせが来ても、合成済み文字がなければ「ア+濁点記号」として落とす。マッチしたら i++ で濁点ぶんを読み飛ばす。

4モードを「関数合成」で組む

このツールには4つの変換モードがある。ひらがな→カタカナ、カタカナ→ひらがな、半角カナ→カタカナ、そして半角カナ→ひらがな

最後の「半角カナ→ひらがな」が面白い。これ専用の変換テーブルを新たに作る必要はない。すでにある2つの関数をつなげるだけで済む。半角カナ→ひらがな は「半角カナ→全角カタカナ」してから「カタカナ→ひらがな」すれば実現できる。

function convert(text: string, mode: ConvertMode): string {
  switch (mode) {
    case 'hiragana-to-katakana': return hiraganaToKatakana(text)
    case 'katakana-to-hiragana': return katakanaToHiragana(text)
    case 'hankaku-to-katakana':  return hankakuToZenkakuKana(text)
    // 半角カナ → 全角カタカナ → ひらがな の2段(関数合成)
    case 'hankaku-to-hiragana':  return katakanaToHiragana(hankakuToZenkakuKana(text))
  }
}

katakanaToHiragana(hankakuToZenkakuKana(text)) の入れ子1行。半角カナを一度「全角カタカナ」という中間表現に正規化してしまえば、あとは既存の 0x60 変換に乗れる。新しいロジックはゼロ。

これは変換系を組むときの定石で、全ての変換を直接実装するのではなく、中間表現(ここでは全角カタカナ)にハブを作り、そこ経由で繋ぐと組み合わせが一気に減る。4モードあっても、実体の変換関数は3つ(ひら→カタ、カタ→ひら、半角→全角カタ)で足りている。半角→ひらがな は合成で導出した4つめ、という構図。

まとめ

かな変換は「ひらがな⇔カタカナは一瞬、半角カナだけ手間」という非対称な題材だった。

  • ひらがな⇔カタカナは Unicode 上で同じ並び。0x60 を足し引きするだけ。テーブル不要・濁音も自動
  • 変換範囲は U+3041-3096 / U+30A1-30F6。末尾の繰り返し記号などを巻き込まないよう範囲を区切る
  • 半角カナはオフセットが効かないのでマッピングテーブル。濁音は の2文字を先読みして合成、無効な組み合わせはフォールバック
  • 多段変換(半角カナ→ひらがな)は専用実装せず、全角カタカナを中間表現にした関数合成で導出する

0x60 の足し引きで済むひらがな・カタカナの素直さと、半角カナの泥臭さの落差が、日本語テキスト処理の縮図のようで面白い題材だった。

ぱんだツールズ では他にも PDF・画像・CSV・テキスト処理など、開発者向けのツールを多数公開している。全部無料・登録不要・ブラウザ完結で使える。
https://sakutto-panda.com


この記事は Zenn にも同じ内容を投稿しています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?