アラビア数字から漢数字に変換と漢数字からアラビア数字へ変換するオブジェクトを書いてみました。
日本の漢字文化ならではの関数です。
アラビア数字から漢数字に変換する関数は、for文を使わずに再帰呼び出しでScala風です。
Listは辞書を作るにはたいへん便利です。
package seedo
import scala.collection.mutable.HashMap
// アラビア数字 <-> 漢数字 変換
object Kansuji {
// 変換規則
val ji = List[(Char,Long,Char)](('〇',0L,'A'),('一',1L,'A'),('二',2L,'A')
,('三',3L,'A'),('四',4L,'A'),('五',5L,'A'),('六',6L,'A'),('七',7L,'A')
,('八',8L,'A'),('九',9L,'A'),('零',0L,'A'),('壱',1L,'A'),('弐',2L,'A')
,('参',3L,'A'),('肆',4L,'A'),('伍',5L,'A'),('陸',6L,'A'),('質',7L,'A')
,('捌',8L,'A'),('玖',9L,'A'),('零',0L,'A'),('壹',1L,'A'),('貳',2L,'A'),('參',3L,'A')
,('0',0L,'A'),('1',1L,'A'),('2',2L,'A'),('3',3L,'A'),('4',4L,'A')
,('5',5L,'A'),('6',6L,'A'),('7',7L,'A'),('8',8L,'A'),('9',9L,'A')
,('0',0L,'A'),('1',1L,'A'),('2',2L,'A'),('3',3L,'A'),('4',4L,'A')
,('5',5L,'A'),('6',6L,'A'),('7',7L,'A'),('8',8L,'A'),('9',9L,'A')
// 桁
,('十',10L,'T'),('百',100L,'T'),('千',1000L,'T'),('拾',10L,'T'),('佰',100L,'T')
,('仟',1000L,'T'),('十',10L,'T'),('陌',100L,'T'),('阡',1000L,'T')
// 桁
,('万',10000L,'M'),('億',100000000L,'M'),('兆',1000000000000L,'M')
,('萬',10000L,'M'),('京',10000000000000000L,'M')
,(',',0L,'U'),(',',0L,'U'))
val dic = new HashMap[Char,(Char,Long,Char)] // 漢数字の辞書
ji.foreach(v => dic.put(v._1,v))
val keta4 = Array("", "十", "百", "千")
val keta = Array("", "万", "億", "兆", "京")
val ketaOld4 = Array("", "拾", "佰", "仟") // (旧漢字)
val ketaOld = Array("", "萬", "億", "兆", "京") // (旧漢字)
// 漢数字に変換
def toKansuji (num:Long) : String = {
var result = ""
var back = ""
var tmp_div = 0 //整数除算
val su = toArabicKansuji(num).reverse
for(i <- 0 to su.length - 1){
val c = su.charAt(i)
if(c != '〇') {
val tmp_mod = i % 4 //余り
if (tmp_mod == 0) { //大きな桁
tmp_div = (i + 1) / 4 //整数除算
result = c + keta(tmp_div) + result
} else { //"十", "百", "千"の桁
if(c == '一'){ // 一を省略
result = keta4(tmp_mod) + back + result
} else {
result = c + keta4(tmp_mod) + back + result
}
}
back = ""
}else{
if ((i % 4) == 0) { //大きな桁
tmp_div = (i + 1) / 4 //整数除算
back = keta(tmp_div)
}
}
}
result
}
// 漢数字(旧漢字)に変換
def toKansujiOld (num:Long) : String = {
var result = ""
var back = ""
var tmp_div = 0 //整数除算
val su = toArabicKansujiOld(num).reverse
for(i <- 0 to su.length - 1){
val c = su.charAt(i)
if(c != '〇') {
val tmp_mod = i % 4 //余り
if (tmp_mod == 0) { //大きな桁
tmp_div = (i + 1) / 4 //整数除算
result =c + ketaOld(tmp_div) + result
} else { //"十", "百", "千"の桁
if(c == '壱'){ // 壱を省略
result = ketaOld4(tmp_mod) + back + result
} else {
result = c + ketaOld4(tmp_mod) + back + result
}
}
back = ""
}else{
if ((i % 4) == 0) { //大きな桁
tmp_div = (i + 1) / 4 //整数除算
back = ketaOld(tmp_div)
}
}
}
result
}
// 整数をアラビア数字表記の漢数字に変換 再帰呼び出し
def toArabicKansuji (num:Long) : String = {
if(num == 0) return "零"
var number = (num % 10) match {
case 0 => "〇"
case 1 => "一"
case 2 => "二"
case 3 => "三"
case 4 => "四"
case 5 => "五"
case 6 => "六"
case 7 => "七"
case 8 => "八"
case 9 => "九"
case _ => ""
}
if(num > 9L) return toArabicKansuji(num / 10) + number
number
}
// 整数をアラビア数字表記の漢数字(旧漢字)に変換 再帰呼び出し
def toArabicKansujiOld (num:Long) : String = {
if(num == 0) return "零"
var number = (num % 10) match {
case 0 => "〇"
case 1 => "壱"
case 2 => "弐"
case 3 => "参"
case 4 => "四"
case 5 => "伍"
case 6 => "六"
case 7 => "七"
case 8 => "八"
case 9 => "九"
case _ => ""
}
if(num > 9L) return toArabicKansujiOld(num / 10) + number
number
}
// 整数(Int)をアラビア数字表記の漢数字に変換
def toArabicKansuji (num:Int) : String = {
toArabicKansuji(num.toLong)
}
// 漢数字を整数に変換
def toArabicNumerals (num:String) : Long = {
var result = 0L // 変換結果
var resultArabic = 0L // アラビア数字表記の漢数字の変換結果
var format = 0 // 0は未確定状態、アラビア数字表記の漢数字の場合は1、2は漢数字、3は万・億・兆・京と〇から九までの漢数字
var backSuji = -1L // 1つ前の数字
var back4 = 0L // 千・万・十の時のバッファ
var backKeta = 0L // 万・億・兆・京の1つ前の桁
var backKeta4 = 0L // 千・百・十の1つ前の桁
var four = 0L // 〇から九までの漢数字が四桁以下の連続の場合のバッファ
var current = -1L
for(i <- 0 to num.length - 1){
val c = num.charAt(i)
// println(" #" + i + " [" + c + "] format=" + format + " backSuji="+backSuji+ " back4="+back4+ " four="+ four + " result=" + result )
val r = dic.get(c) match {
case Some(f) => f
case _ => ('0',0L,'X') // 漢数字を構成する文字列がない
}
current = r._2
r._3 match {
case 'A' => {
resultArabic = current + (resultArabic * 10L)
four = current + (four * 10L)
if(format != 1 && four > 9999){
return -8L // 4桁以上は構文エラー
}
if(i > 0){
if(format == 0 && backKeta == 0L){
format = 1 // フォーマット確定
}
}
backSuji =current // 一つ前として記録
}
case 'T' => { // 千・百・十 漢数字の4桁バッファの処理
format = 2
if(backKeta4 != 0L && backKeta4 <= current){
return -5L // 桁が1つ前の桁よりも大きいか同じなら構文エラー
}
if(backSuji > 0L){
back4 += backSuji * current // 千・百・十の前に数字がある
} else {
back4 += current // 千・百・十が単独
}
backKeta4 = current
backSuji = -1L // 初期化
four = 0L // 初期化 〇から九までの漢数字が四桁以下の連続の場合のバッファ
}
case 'M' => { // 万・億・兆・京
if(backSuji == -1L && back4 == 0L){
return -7L // 1つ前が数字でないか、千・百・十でないときは構文エラー
}
if(backKeta != 0L && backKeta <= current){
return -4L // 現在の桁が1つ前の桁よりも大きいか同じなら構文エラー
}
if(format == 2){
if(backSuji > 0L){
back4 += backSuji
}
result += (back4 * current)
} else if(four > 0L){
if(four > 9999L){ // 4桁以上なら構文エラー
return -8L
}
result += (four * current)
format = 3 // 書式が確定
}
backKeta = current // 一つ前の文字として保存
backKeta4 = 0L // 初期化
backSuji = -1L // 初期化
back4 = 0L // 初期化
four = 0L // 初期化 〇から九までの漢数字が四桁以下の連続の場合のバッファ
}
case 'U' =>
case _ => return -9L // 漢数字ではない
}
}
if(format == 2){// 千・万・十の4桁後処理
if(backSuji > 0L)
back4 += backSuji
result += back4
} else {
result += four
}
if(format == 1) // アラビア数字表記の漢数字の変換結果
return resultArabic
result
}
}
下記は、Scala Testを使って上記オブジェクトをテストするクラスです。
最初のテストは、アラビア数字からアラビア数字風漢数字(一二三四五六〇〇)、
漢数字(千二百三十四万五千六百)、旧漢字(壱千弐百参拾四萬五千六百)を変換して
再びアラビア数字に変換するというものです。
次のテストは、Loopでアラビア数字を回して、アラビア数字から漢数字、漢数字から再びアラビア数字の戻してます。
最後のテストは、漢数字からアラビア数字を変換する関数で、正常系と異常系をテストケースにします。
テストケースのデータもListで定義すると便利。
package test
import org.scalatest.junit.JUnitSuite
import org.scalatest.junit.ShouldMatchersForJUnit
import org.junit.{Test,Before}
import seedo._
class testKansuji extends JUnitSuite with ShouldMatchersForJUnit{
val test = List[(String,Long)](
("千二百三十四兆五千六百七十八億九千十二万三千四百五十六",1234567890123456L)
,("千百三十四兆千百十八億九千十二万三千四百五十六",1134111890123456L)
,("二億三千九十万十二",230900012L)
,("二億三千九千万十二",-5L) // 桁が1つ前の桁よりも大きいか同じ
,("二億三千九百万八十",239000080L)
,("二億三千九十万十二兆",-4L) // 億の単位の後に兆の単位がある
,("億三千九十万十二",-7L) // 億の前に数字がない
,("一二三四五六七八九〇一二三",1234567890123L)
,("一,二三四,五六七,八九〇,一二三",1234567890123L)
,("一二三四五六七八九〇万一二三",-8L) // 4桁以上なら構文エラー
,("一万二三四五六七八九〇一二三",-8L) // 4桁以上なら構文エラー
,("〇〇二三四五六七八九〇一二三",234567890123L)
,("〇万〇二三四五六七八九〇一二三",-8L) // 4桁以上なら構文エラー
,("万〇二三四五六七八九〇一二三",-7L) // 万の前に数字がない
,("〇" ,0L)
,("壱" ,1L)
,("弐" ,2L)
,("参" ,3L)
,("拾" ,10L)
,("阡" ,1000L)
,("壱萬" ,10000L)
,("参億" ,300000000L)
,("参億四" ,300000004L)
,("参億四十三" ,300000043L)
,("672兆1,293億" ,672129300000000L)
,("92兆2,292億346",92229200000346L)
,("漢数字" ,-9L) // 漢数字ではない
,("百日二左衛門",-9L) // 漢数字ではない
)
val test2 = List[(Long,(String,String,String))](
(10000L ,("一〇〇〇〇","一万","壱萬"))
,(100000000L ,("一〇〇〇〇〇〇〇〇","一億","壱億"))
,(100000L ,("一〇〇〇〇〇","十万","拾萬"))
,(1234567890L ,("一二三四五六七八九〇","十二億三千四百五十六万七千八百九十","拾弐億参仟四佰伍拾六萬七仟八佰九拾"))
,(1234567893L ,("一二三四五六七八九三","十二億三千四百五十六万七千八百九十三","拾弐億参仟四佰伍拾六萬七仟八佰九拾参"))
,(1111L ,("一一一一","千百十一","仟佰拾壱"))
,(11111111111111111L ,("一一一一一一一一一一一一一一一一一"
,"一京千百十一兆千百十一億千百十一万千百十一","壱京仟佰拾壱兆仟佰拾壱億仟佰拾壱萬仟佰拾壱"))
,(0L ,("零","零","零"))
,(-0L ,("零","零","零"))
)
@Before def initialize() {
}
@Test def toArabicKansuji() { //
println("TEST toArabicKansuji")
test2.foreach({v =>
val sample = v._1
println(sample)
val resultvalue = Kansuji.toArabicKansuji(sample)
val resultvalue2 = Kansuji.toKansuji(sample)
val resultvalue3 = Kansuji.toKansujiOld(sample)
println(v._2._1 + " -> [" + resultvalue + "]" + v._2._1.length + "/" + resultvalue.length)
v._2._1 should be (resultvalue)
sample should be (Kansuji.toArabicNumerals(v._2._1))
println(v._2._2 + " -> [" + resultvalue2 + "]" + v._2._2.length + "/" + resultvalue2.length)
v._2._2 should be (resultvalue2)
sample should be (Kansuji.toArabicNumerals(v._2._2))
println(v._2._3 + " -> [" + resultvalue3 + "]" + v._2._3.length + "/" + resultvalue3.length)
v._2._3 should be (resultvalue3)
sample should be (Kansuji.toArabicNumerals(v._2._3))
println
})
}
@Test def toArabicKansujiLoop() { //
println("TEST toArabicKansuji Loop Start " + new java.util.Date)
var i:Long = 0L
var num,num2,num3 = 0L
var up = 1L
var ch = 100000L
while(i < 999999999999999999L) {
val resultvalue = Kansuji.toArabicKansuji(i)
val resultvalue2 = Kansuji.toKansuji(i)
val resultvalue3 = Kansuji.toKansujiOld(i)
num = Kansuji.toArabicNumerals(resultvalue)
i should be (num)
num2 = Kansuji.toArabicNumerals(resultvalue2)
if(i != num2)
println("TEST toArabicKansuji " + i + " <> " + num2 + " [" +resultvalue2 + "]")
i should be (num2)
num3 = Kansuji.toArabicNumerals(resultvalue3)
if(i != num3)
println("TEST toArabicKansuji " + i + " <> " + num3 + " [" +resultvalue3 + "]")
i should be (num3)
if((i % ch) == 0) {
println("TEST toArabicKansuji " + i + " " + new java.util.Date)
up *= 10L
ch *= 10L
}
i += up
}
println("TEST toArabicKansuji End " + new java.util.Date)
}
@Test def toArabicNumerals() { //
println("TEST toArabicNumerals")
test.foreach({v =>
val resultvalue = Kansuji.toArabicNumerals(v._1)
val sa = resultvalue - v._2
println(v + " -> " + resultvalue + " sa=" + sa)
sa should be (0)
})
println
}
}