Skip to content

Instantly share code, notes, and snippets.

@nimzo6689
Last active December 9, 2018 10:03
Show Gist options
  • Save nimzo6689/e4d55591d71174d428070d4f5b931924 to your computer and use it in GitHub Desktop.
Save nimzo6689/e4d55591d71174d428070d4f5b931924 to your computer and use it in GitHub Desktop.
数字の読み方を返すプログラム
function ConvertTo-NumberRuby {
[CmdletBinding()]
param(
[Parameter(
Mandatory = $True,
ValueFromPipeline = $True,
ValueFromPipelineByPropertyName = $True
)]
[string[]]$Sources
)
process {
foreach ($src in $Sources) {
$(
for ($digit = $src.Length; $digit -gt 0; $digit--) {
$number = [Char]::GetNumericValue($src[$src.Length - $digit])
switch ($number) {
0 {}
1 {
switch ($digit % 4) {
1 {'いち'}
0 {'いっ'}
default {}
}
}
2 { 'に' }
3 { 'さん' }
4 { 'よん' }
5 { 'ご' }
6 {
if ($digit -band 3 -band 2) {'ろっ'} else {'ろく'}
}
7 { 'なな' }
8 {
if ($digit -band 3 -band 2) {'はっ'} else {'はち'}
}
9 { 'きゅう' }
}
switch ($digit) {
13 { 'ちょう' }
9 { 'おく' }
5 { 'まん' }
{$_ % 4 -eq 0} {
if ($number -eq 3) {'ぜん'} else {'せん'}
}
{$_ % 4 -eq 3} {
switch ($number) {
3 {'びゃく'}
{$_ -in (6, 8)} {'ぴゃく'}
default {'ひゃく'}
}
}
{$_ % 4 -eq 2} { "じゅう" }
}
}
) -join ''
}
}
}
549832, 418313801, 15056, 10001000 | ConvertTo-NumberRuby
var NumberRuby = NumberRuby || {};
(function (_) {
_.toLargeDigitRuby = function (digit) {
if (digit & 3) {
return "";
}
switch (digit >> 2) {
case 0:
return "";
case 1:
return "まん";
case 2:
return "おく";
case 3:
return "ちょう";
default:
console.error("「京」以降はサポート対象外です。");
}
}
_.toSmallDigitRuby = function (number, digit) {
let _num = Number(number);
switch (_num) {
case 0:
return "";
case 1:
switch (digit & 3) {
case 1:
case 2:
return "";
}
}
switch (digit & 3) {
case 0:
return "";
case 1:
return "じゅう";
case 2:
switch (_num) {
case 3:
return "びゃく";
case 6:
case 8:
return "ぴゃく";
}
return "ひゃく";
break;
case 3:
if (_num === 3) {
return "ぜん";
}
return "せん";
}
}
_.toNumberRuby = function (number, digit) {
switch (Number(number)) {
case 0:
return "";
case 1:
switch (digit & 3) {
case 1:
case 2:
return "";
case 3:
return "いっ";
}
return "いち";
case 2:
return "に";
case 3:
return "さん";
case 4:
return "よん";
case 5:
return "ご";
case 6:
if (digit & 3 & 2) {
return "ろっ";
}
return "ろく";
case 7:
return "なな";
case 8:
if (digit & 3 & 2) {
return "はっ";
}
return "はち";
case 9:
return "きゅう";
}
}
_.from = function (numbers) {
return ('' + numbers).split('')
.reverse()
.map((value, index) => _.toNumberRuby(value, index)
+ _.toSmallDigitRuby(value, index) + _.toLargeDigitRuby(index))
.reverse()
.join('');
}
}
)(NumberRuby);
console.log(NumberRuby.from(58095165));
console.log(NumberRuby.from(418313801));
console.log(NumberRuby.from(15056));
console.log(NumberRuby.from(10001000));
package com.qiita.nimzo6689;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* 数字の送り仮名を返す。<br>
* 対象となる数字:1~1京未満までの自然数
*
* @see 数字の読み方について<br>
* https://www.coscom.co.jp/learnjapanese101/wordcategory/basicwords_numbers-c.html
* @author nimzo6689
*/
public class NumberReading {
private final long source;
private SmallDigitReader smallDigitReader;
private LargeDigitReader largeDigitReader;
public NumberReading(long source) {
this.source = source;
this.smallDigitReader = SmallDigitReader.ONES;
}
public static void main(String[] args) {
Stream.of(58_095_165L, 800_418_313_801L, 15_056L, 10_001_000L)
.map(v -> String.format("%,d", v) + "=" + new NumberReading(v).say())
.forEach(System.out::println);
}
public String say() {
final StringBuilder ansBuilder = new StringBuilder();
long unreadSource = source;
for (int i = 0; i < String.valueOf(source).length(); i++) {
// まだ読み方を取得していない数列の一番右にある数字を取得。
NumberReader numberReader = NumberReader.of((int) (unreadSource % 10));
// 読み方を取得
if (i % 4 == 0) {
largeDigitReader = largeDigitReader == null
? LargeDigitReader.NONE : largeDigitReader.increment();
ansBuilder.insert(0, largeDigitReader.say());
}
ansBuilder.insert(0, smallDigitReader.say(numberReader));
ansBuilder.insert(0, numberReader.say(smallDigitReader, largeDigitReader));
// インクリメント
smallDigitReader = smallDigitReader.rotate();
unreadSource = unreadSource / 10;
}
return ansBuilder.toString();
}
private enum LargeDigitReader {
NONE(""),
MAN("まん"),
OKU("おく"),
CHO("ちょう"),;
// 「京」以降は省略。
private final String reading;
private LargeDigitReader(String reading) {
this.reading = reading;
}
public String say() {
return reading;
}
public final LargeDigitReader increment() {
if (LargeDigitReader.values().length == ordinal() + 1) {
throw new IllegalStateException("「京」以降の桁はサポート対象外です。");
}
return LargeDigitReader.values()[ordinal() + 1];
}
}
private enum SmallDigitReader {
ONES(""),
TENS("じゅう"),
HUNDREDS(n -> {
switch (n) {
case THREE:
return "びゃく";
case SIX:
case EIGHT:
return "ぴゃく";
default:
return "ひゃく";
}
}),
THOUSANDS(n -> n == NumberReader.THREE ? "ぜん" : "せん"),;
private final Function<NumberReader, String> reader;
private SmallDigitReader(Function<NumberReader, String> reader) {
this.reader = reader;
}
private SmallDigitReader(String reading) {
this((n) -> reading);
}
public String say(NumberReader number) {
return number == NumberReader.ZERO ? "" : reader.apply(number);
}
// 「一」→「十」→「百」→「千」→「一」→...
public final SmallDigitReader rotate() {
return SmallDigitReader.values().length == ordinal() + 1
? ONES : SmallDigitReader.values()[ordinal() + 1];
}
}
private enum NumberReader {
ZERO(0, ""),
ONE(1, (s, l) -> {
switch (s) {
case TENS:
case HUNDREDS:
return "";
case THOUSANDS:
return "いっ";
default:
return l == LargeDigitReader.CHO
? "いっ" : "いち";
}
}),
TWO(2, "に"),
THREE(3, "さん"),
FOUR(4, "よん"),
FIVE(5, "ご"),
SIX(6, (s, l) -> s == SmallDigitReader.HUNDREDS ? "ろっ" : "ろく"),
SEVEN(7, "なな"),
EIGHT(8, (s, l)
-> (s == SmallDigitReader.HUNDREDS || s == SmallDigitReader.THOUSANDS)
|| (l == LargeDigitReader.CHO && s == SmallDigitReader.ONES)
? "はっ" : "はち"
),
NINE(9, "きゅう"),;
private static final Map<Integer, NumberReader> VALUES;
static {
VALUES = Arrays.stream(NumberReader.values()).collect(
Collectors.toMap(d -> d.number, d -> d)
);
}
private final int number;
private final BiFunction<SmallDigitReader, LargeDigitReader, String> reader;
private NumberReader(int number, BiFunction<SmallDigitReader, LargeDigitReader, String> reader) {
this.number = number;
this.reader = reader;
}
private NumberReader(int number, String reading) {
this(number, (s, l) -> reading);
}
public static final NumberReader of(int value) {
if (VALUES.containsKey(value)) {
return VALUES.get(value);
}
throw new IllegalArgumentException(
"Type:" + value + " is not a valid " + NumberReader.class.getName() + " value.");
}
public String say(SmallDigitReader smallDigit, LargeDigitReader largeDigit) {
return reader.apply(smallDigit, largeDigit);
}
}
}
#!/bin/bash
# 数字の読み方について
# https://www.coscom.co.jp/learnjapanese101/wordcategory/basicwords_numbers-c.html
function read_number() {
local readonly SOURCES=$(
if [ -p /dev/stdin ]; then cat -; else echo "$@"; fi
)
for source in ${SOURCES[@]}; do
echo $source \
| sed 's/[0-9]/&\n/g' \
| tac \
| grep '[0-9]' \
| sed -e '5s/.*/&まん/' -e '9s/.*/&おく/' -e '13s/.*/&ちょう/' \
| sed -e '2~4s/.*/&じゅう/' \
| sed -e '3~4s/.*/&ひゃく/' -e 's/3ひゃく/3びゃく/' -e 's/6ひゃく/6ぴゃく/' -e 's/8ひゃく/8ぴゃく/' \
| sed -e '4~4s/.*/&せん/' -e 's/3せん/3ぜん/' \
| sed -e 's/0.*\(まん\|おく\|ちょう\)/\1/' -e 's/0.*//' \
| sed -e 's/1/いち/' -e '2~4s/いち.*//' -e '3~4s/いち.*//' -e '4~4s/いち/いっ/' -e '9s/いち/いっ/' \
| sed -e 's/2/に/' \
| sed -e 's/3/さん/' \
| sed -e 's/4/よん/' \
| sed -e 's/5/ご/' \
| sed -e 's/6/ろく/' -e '3~4s/ろく/ろっ/' \
| sed -e 's/7/なな/' \
| sed -e 's/8/はち/' -e '3~4s/はち/はっ/' -e '4~4s/はち/はっ/' \
| sed -e 's/9/きゅう/' \
| tac \
| paste -sd'\0'
done
}
cat << EOF | read_number
58095165
418313801
15056
10001000
EOF
-- PostgreSQL9.2以降ではSQL関数でも引数に名前を指定できますが、便宜上$nスタイルを使用してます。
CREATE OR REPLACE FUNCTION numerical_to_reading(integer) RETURNS varchar AS $$
-- 数字の読み方を出力する。
WITH RECURSIVE
-- 数字の読み方を定義
-- 参考: https://www.coscom.co.jp/learnjapanese101/wordcategory/basicwords_numbers-c.html
numeral_reading(numeral, reading) AS (
VALUES
(0, ''),
(1, 'いち'), (2, 'に'), (3, 'さん'),
(4, 'よん'), (5, 'ご'), (6, 'ろく'),
(7, 'なな'), (8, 'はち'), (9, 'きゅう')
),
-- 1桁ごとの読み方を定義(一、十、百、千)
small_digit(digit, reading) AS (
VALUES
(0, ''), -- 一の位の数については呼称なし。
(1, 'じゅう'), (2, 'ひゃく'), (3, 'せん')
),
-- 例外的な数字の読み方
patch_small_digit_numeral(digit, numeral, reading) AS (
VALUES
(1, 1, ''), (2, 1, ''), (3, 1, 'いっ'), -- 十、百、千(いっせん)の読み方を定義
(2, 6, 'ろっ'), -- 6百(ろっぴゃく)の読み方を定義
(2, 8, 'はっ'), (3, 8, 'はっ') -- 8百(はっせん)、8千(はっせん)の読み方を定義
),
-- 例外的な桁の読み方
patch_small_digit(digit, numeral, reading) AS (
VALUES
(1, 0, ''), (2, 0, ''), (3, 0, ''), -- 0の場合は十百千は読まない
(2, 3, 'びゃく'), (3, 3, 'ぜん'), -- 3百(さんびゃく)、3千(さんぜん)の読み方を定義
(2, 6, 'ぴゃく'), (2, 8, 'ぴゃく') -- 6百(ろっぴゃく)、8百(はっぴゃく)の読み方を定義
),
-- 4桁区切りの桁ごとの読み方を定義(万、億、兆、...)
large_digit(digit, reading) AS (
VALUES
(4, 'まん'), (8, 'おく') -- 「兆」以降は省略。
),
-- args.numeralに指定された数字を桁ごとに分割する。
digit_numeral_mapping(digit, numeral) AS (
SELECT
0,
substring(
abs($1)::varchar from char_length(abs($1)::varchar) for 1
)::integer
UNION ALL
SELECT
dnm.digit + 1,
substring(
abs($1)::varchar from char_length(abs($1)::varchar) - (dnm.digit + 1) for 1
)::integer
FROM digit_numeral_mapping AS dnm
WHERE dnm.digit < char_length(abs($1)::varchar) - 1
),
-- 桁ごとに数字の読み方を出力する。
readings_per_digit(digit, reading) AS (
SELECT
dnm.digit,
COALESCE(psdn.reading, nr.reading) -- 0~9までの数字
|| COALESCE(psd.reading, sd.reading) -- 十百千の位
|| COALESCE(ld.reading, '') -- 万、億の位
FROM digit_numeral_mapping AS dnm
NATURAL JOIN numeral_reading AS nr
JOIN small_digit AS sd
ON sd.digit = dnm.digit % 4
LEFT JOIN large_digit AS ld
ON ld.digit = dnm.digit
LEFT JOIN patch_small_digit_numeral AS psdn
ON psdn.digit = dnm.digit % 4
AND psdn.numeral = dnm.numeral
LEFT JOIN patch_small_digit AS psd
ON psd.digit = dnm.digit % 4
AND psd.numeral = dnm.numeral
ORDER BY dnm.digit DESC
)
-- 数字の読み方を出力
SELECT
CASE
WHEN $1 = 0 THEN 'ぜろ'
WHEN $1 < 0 THEN 'まいなす'
ELSE ''
END || array_to_string(array_agg(reading ORDER BY digit DESC), '') AS reading
FROM readings_per_digit
;
$$ LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
VALUES
(0, numerical_to_reading(0)),
(-15056, numerical_to_reading(-15056)),
(10001000, numerical_to_reading(10001000)),
(58095165, numerical_to_reading(58095165)),
(418313801, numerical_to_reading(418313801))
;
-- 0 "ぜろ"
-- -15056 "まいなすいちまんごせんごじゅうろく"
-- 10001000 "いっせんまんいっせん"
-- 58095165 "ごせんはっぴゃくきゅうまんごせんひゃくろくじゅうご"
-- 418313801 "よんおくいっせんはっぴゃくさんじゅういちまんさんぜんはっぴゃくいち"
-- ※PL/pgSQLです。
CREATE OR REPLACE FUNCTION numerical_to_reading(numerical_value integer) RETURNS varchar AS $$
DECLARE
numerical_char CONSTANT varchar := abs(numerical_value)::varchar;
numerical_length CONSTANT integer := char_length(numerical_char);
digit integer;
currval integer;
ret varchar := '';
BEGIN
IF numerical_value = 0 THEN
-- 一桁の0の場合のみ「ぜろ」が読まれるため、すぐにRETURNで返す。
RETURN 'ぜろ';
ELSIF numerical_value < 0 THEN
-- マイナス符号の読み方を定義
ret := ret || 'まいなす';
END IF;
-- 整数部の読み方を求める
FOR i IN 1 .. numerical_length LOOP
digit := numerical_length - i + 1;
currval := substring(numerical_char FROM i FOR 1)::integer;
-- 数字の読み方を逐次解釈
-- 参考: https://www.coscom.co.jp/learnjapanese101/wordcategory/basicwords_numbers-c.html
CASE currval
WHEN 1 THEN
CASE digit % 4
WHEN 0 THEN
-- 1,000(いっせん)の読み方を定義
ret := ret || 'いっ';
WHEN 1 THEN
ret := ret || 'いち';
ELSE
-- 10, 100の場合は1自体は読まない。
NULL;
END CASE;
WHEN 2 THEN
ret := ret || 'に';
WHEN 3 THEN
ret := ret || 'さん';
WHEN 4 THEN
ret := ret || 'よん';
WHEN 5 THEN
ret := ret || 'ご';
WHEN 6 THEN
IF digit % 4 = 3 THEN
-- 6百(ろっぴゃく)の読み方を定義
ret := ret || 'ろっ';
ELSE
ret := ret || 'ろく';
END IF;
WHEN 7 THEN
ret := ret || 'なな';
WHEN 8 THEN
IF digit % 4 IN (3, 0) THEN
-- 8百(はっせん)、8千(はっせん)の読み方を定義
ret := ret || 'はっ';
ELSE
ret := ret || 'はち';
END IF;
WHEN 9 THEN
ret := ret || 'きゅう';
ELSE
NULL;
END CASE;
-- 1桁ごとの読み方を定義(一、十、百、千)
IF currval = 0 THEN
NULL;
ELSE
CASE digit % 4
WHEN 2 THEN
ret := ret || 'じゅう';
WHEN 3 THEN
IF currval = 3 THEN
-- 3百(さんびゃく)の読み方を定義
ret := ret || 'びゃく';
ELSIF currval IN (6, 8) THEN
-- 6百(ろっぴゃく)、8百(はっぴゃく)の読み方を定義
ret := ret || 'ぴゃく';
ELSE
ret := ret || 'ひゃく';
END IF;
WHEN 0 THEN
IF currval = 3 THEN
-- 3千(さんぜん)の読み方を定義
ret := ret || 'ぜん';
ELSE
ret := ret || 'せん';
END IF;
ELSE
NULL;
END CASE;
END IF;
-- 4桁ごとにつく単位の読み方を定義(万、億)※兆以降はサポート対象外
CASE digit
WHEN 5 THEN
ret := ret || 'まん';
WHEN 9 THEN
ret := ret || 'おく';
ELSE
NULL;
END CASE;
END LOOP;
RETURN ret;
END
$$ LANGUAGE plpgsql
IMMUTABLE
RETURNS NULL ON NULL INPUT;
VALUES
(0, numerical_to_reading(0)),
(-15056, numerical_to_reading(-15056)),
(10001000, numerical_to_reading(10001000)),
(58095165, numerical_to_reading(58095165)),
(418313801, numerical_to_reading(418313801))
;
-- 0 "ぜろ"
-- -15056 "まいなすいちまんごせんごじゅうろく"
-- 10001000 "いっせんまんいっせん"
-- 58095165 "ごせんはっぴゃくきゅうまんごせんひゃくろくじゅうご"
-- 418313801 "よんおくいっせんはっぴゃくさんじゅういちまんさんぜんはっぴゃくいち"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment