1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptで数字を漢数字に変換する:万・億の「4桁区切り」を実装で理解する

1
Posted at

はじめに

日本語の数字表記をプログラムで扱うと、英語の感覚では意外なところでつまずきます。英語は 1,000 ごとに thousand / million / billion と単位が変わりますが、日本語は 10,000(万)ごとに単位が変わります。この「4桁区切り」を理解しておくと、漢数字への変換はかなりシンプルに書けます。

完成形の動きを先に確認したい場合は、数字を入力すると漢字表記とローマ字の読みを表示してくれる DigitalKW の Japanese Number Converter が参考になります。この記事では、同じような変換を JavaScript でゼロから組み立てながら、どこに例外があるのかを整理していきます。

日本語の数字の構造

日本語の数は、次の 2 種類の単位の組み合わせでできています。

種類 単位 値
小さい単位 十・百・千 10, 100, 1000
大きい単位 万・億・兆・京 10^4, 10^8, 10^12, 10^16

たとえば 123,456,789 は、4桁ずつ区切ると 1 | 2345 | 6789 になります。

  • 1 → 一 + 億
  • 2345 → 二千三百四十五 + 万
  • 6789 → 六千七百八十九

つなげると「一億二千三百四十五万六千七百八十九」です。英語のように 3 桁区切り(123,456,789)で考えると、億や万の位置がずれてしまうので注意が必要です。

4桁ブロックを変換する

まず 0〜9999 の範囲だけを変換する関数を作ります。ポイントは「1 のときは数字を書かない」というルールです。10 は「一十」ではなく「十」、100 は「一百」ではなく「百」と書きます。

const DIGITS = ["", "一", "二", "三", "四", "五", "六", "七", "八", "九"];
const SMALL = ["", "十", "百", "千"];

function fourDigits(n) {
  let s = "";
  for (let i = 3; i >= 0; i--) {
    const d = Math.floor(n / 10 ** i) % 10;
    if (d === 0) continue; // 0 の位は何も書かない
    s += (d === 1 && i > 0 ? "" : DIGITS[d]) + SMALL[i];
  }
  return s;
}

0 の位を飛ばすだけで、105 は「百五」、2026 は「二千二十六」になります。アラビア数字のように「〇」で桁を埋める必要はありません。

大きい単位をつなげる

次に、数字全体を 10000 で割りながら 4 桁ずつ取り出し、ブロックごとに万・億・兆を付けます。兆を超える値は Number の安全な整数の範囲(約 9007 兆)に近づくので、最初から BigInt で扱っておくと安心です。

const LARGE = ["", "万", "億", "兆", "京"];

function toKansuji(value) {
  let n = BigInt(value);
  if (n === 0n) return "零";
  let out = "";
  let unit = 0;
  while (n > 0n) {
    const chunk = Number(n % 10000n);
    if (chunk !== 0) out = fourDigits(chunk) + LARGE[unit] + out;
    n /= 10000n;
    unit++;
  }
  return out;
}

ブロックが 0 のときに単位ごとスキップしているのが大事なところです。これがないと 300,000,000 が「三億万」のようなおかしな表記になります。

実行結果は次のとおりです。

             0 零
            10 十
           105 百五
          2026 二千二十六
         10000 一万
         10500 一万五百
     123456789 一億二千三百四十五万六千七百八十九
     300000000 三億
 1000000000000 一兆

10000 は「万」ではなく「一万」になります。小さい単位(十・百・千)では 1 を省略しますが、大きい単位では「一万」「一億」と書くのが普通です。上のコードでは、チャンクが 1 のとき fourDigits(1) が「一」を返すので、自然にこのルールを満たしています。

「一千万」か「千万」か

迷いやすいのが 10,000,000 です。コードの出力は「千万」ですが、金額や公的な文書では「一千万」と書く場面も多くあります。どちらも誤りではないので、用途に合わせて fourDigits の先頭の千にだけ「一」を付けるオプションを用意しておくと便利です。小切手や契約書のように改ざんを防ぎたい場合は、さらに「壱・弐・参・拾」などの大字を使うこともあります。

読み方(ローマ字)の例外

漢字表記は機械的に作れますが、読み方には音の変化があります。代表的なものはこのあたりです。

数字 読み 変化
300 sanbyaku h → b
600 roppyaku 促音 + p
800 happyaku 促音 + p
3000 sanzen s → z
8000 hassen 促音

単純に 数字の読み + "hyaku" で連結すると「sanhyaku」「rokuhyaku」になってしまうので、例外だけを辞書で持つのが現実的です。

const READ_DIGIT = ["", "ichi", "ni", "san", "yon", "go", "roku", "nana", "hachi", "kyuu"];

function readFour(n) {
  const parts = [];
  const th = Math.floor(n / 1000), h = Math.floor(n / 100) % 10,
        t = Math.floor(n / 10) % 10, o = n % 10;
  if (th) parts.push({ 1: "sen", 3: "sanzen", 8: "hassen" }[th] || READ_DIGIT[th] + "sen");
  if (h) parts.push({ 1: "hyaku", 3: "sanbyaku", 6: "roppyaku", 8: "happyaku" }[h] || READ_DIGIT[h] + "hyaku");
  if (t) parts.push(t === 1 ? "juu" : READ_DIGIT[t] + "juu");
  if (o) parts.push(READ_DIGIT[o]);
  return parts.join(" ");
}

console.log(readFour(1984)); // sen kyuuhyaku hachijuu yon

4 と 7 と 9 には「shi / yon」「shichi / nana」「ku / kyuu」の 2 通りの読みがあります。数を数えるときや単位の前では「yon」「nana」「kyuu」が使われることが多いため、ここではそちらに統一しました。日付(四月 = shigatsu)や時刻(九時 = kuji)では別の読みになるので、用途が決まっている場合は辞書を分けるのがおすすめです。

まとめ

  • 日本語の数字は 3 桁ではなく 4 桁ごとに単位が変わる
  • 十・百・千の前の「一」は省略し、万・億の前の「一」は省略しない
  • ブロックが 0 のときは大きい単位ごと書かない
  • 兆を超える値は BigInt で扱う
  • 読み方は 300・600・800・3000・8000 などの音便を辞書で処理する

この 5 点を押さえておけば、請求書の金額表示や日本語学習アプリのような場面でも、漢数字の変換で困ることはほとんどありません。逆方向(漢数字 → アラビア数字)の変換も、同じ 4 桁ブロックの考え方で、万・億の位置で区切ってから千・百・十を足し合わせれば実装できます。

1
1
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
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?