Java implementation of the amount in words
Is there a library implementation of translating sums into words in Russian? If not, how to implement it in the optimal way?
12
Author: Alex Karasev, 2015-09-08
5 answers
You can use the ready-made library Icu4j:
RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("ru"),
RuleBasedNumberFormat.SPELLOUT);
System.out.println(nf.format(1234567));
// один миллион двести тридцать четыре тысячи пятьсот шестьдесят семь
RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("pl"),
RuleBasedNumberFormat.SPELLOUT);
System.out.println(nf.format(1234567));
// jeden milion dwieście trzydzieści cztery tysiące pięćset sześćdziesiąt siedem
RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("en"),
RuleBasedNumberFormat.SPELLOUT);
System.out.println(nf.format(1234567));
// one million two hundred thirty-four thousand five hundred sixty-seven
RuleBasedNumberFormat nf = new RuleBasedNumberFormat(Locale.forLanguageTag("de"),
RuleBasedNumberFormat.SPELLOUT);
System.out.println(nf.format(1234567));
// eine Million zwei-hundert-vier-und-dreißig-tausend-fünf-hundert-sieben-und-sechzig
17
Author: Tagir Valeev, 2015-09-08 09:50:48
The topic is old, but I suggest another option(with recursion):
/**
* Класс для преобразования double-числа в рубли-копейки прописью
* @author Segr88
*/
public class MoneyInWords {
private static final String dig1[][] = {{"одна", "две", "три", "четыре", "пять", "шесть", "семь", "восемь", "девять"},
{"один", "два"}}; //dig[0] - female, dig[1] - male
private static final String dig10[] = {"десять","одиннадцать", "двенадцать", "тринадцать", "четырнадцать",
"пятнадцать", "шестнадцать", "семнадцать", "восемнадцать", "девятнадцать"};
private static final String dig20[] = {"двадцать", "тридцать", "сорок", "пятьдесят",
"шестьдесят", "семьдесят", "восемьдесят", "девяносто"};
private static final String dig100[] = {"сто","двести", "триста", "четыреста", "пятьсот",
"шестьсот", "семьсот", "восемьсот", "девятьсот"};
private static final String leword[][] = { {"копейка", "копейки", "копеек", "0"},
{"рубль", "рубля", "рублей", "1"},
{"тысяча", "тысячи", "тысяч", "0"},
{"миллион", "миллиона", "миллионов", "1"},
{"миллиард", "миллиарда", "миллиардов", "1"},
{"триллион", "триллиона", "триллионов", "1"}};
//рекурсивная функция преобразования целого числа num в рубли
private static String num2words(long num, int level) {
StringBuilder words = new StringBuilder(50);
if (num==0) words.append("ноль "); //исключительный случай
int sex = leword[level][3].indexOf("1")+1; //не красиво конечно, но работает
int h = (int)(num%1000); //текущий трехзначный сегмент
int d = h/100; //цифра сотен
if (d>0) words.append(dig100[d-1]).append(" ");
int n = h%100;
d = n/10; //цифра десятков
n = n%10; //цифра единиц
switch(d) {
case 0: break;
case 1: words.append(dig10[n]).append(" ");
break;
default: words.append(dig20[d-2]).append(" ");
}
if (d==1) n=0; //при двузначном остатке от 10 до 19, цифра едициц не должна учитываться
switch(n) {
case 0: break;
case 1:
case 2: words.append(dig1[sex][n-1]).append(" ");
break;
default: words.append(dig1[0][n-1]).append(" ");
}
switch(n) {
case 1: words.append(leword[level][0]);
break;
case 2:
case 3:
case 4: words.append(leword[level][1]);
break;
default: if((h!=0)||((h==0)&&(level==1))) //если трехзначный сегмент = 0, то добавлять нужно только "рублей"
words.append(leword[level][2]);
}
long nextnum = num/1000;
if(nextnum>0) {
return (num2words(nextnum, level+1) + " " + words.toString()).trim();
} else {
return words.toString().trim();
}
}
//функция преобразования вещественного числа в рубли-копейки
//при значении money более 50-70 триллионов рублей начинает искажать копейки, осторожней при работе такими суммами
public static String inwords(double money) {
if (money<0.0) return "error: отрицательное значение";
String sm = String.format("%.2f", money);
String skop = sm.substring(sm.length()-2, sm.length()); //значение копеек в строке
int iw;
switch(skop.substring(1)) {
case "1": iw = 0;
break;
case "2":
case "3":
case "4": iw = 1;
break;
default: iw = 2;
}
long num = (long)Math.floor(money);
if (num<1000000000000000l) {
return num2words(num, 1) + " " + skop + " " + leword[0][iw];
} else
return "error: слишком много рублей " + skop + " " + leword[0][iw];
}
}
Usage Example:
System.out.println(MoneyInWords.inwords(67382645834.35));
Output:
Sixty-seven billion three hundred eighty-two million six hundred forty-five thousand eight hundred thirty-four rubles 35 kopecks
7
Author: segr88, 2016-12-17 18:57:53
I use this class. You can search for something similar on github
package net.sf.lab3f.util;
import java.util.Stack;
public class RussianMoney {
private static enum Ranges {UNITS, DECADES, HUNDREDS, THOUSANDS, MILLIONS, BILLIONS};
private static Stack <ThreeChar> threeChars;
private static class ThreeChar {
char h, d, u;
Ranges range;
}
public static String digits2Text(Double d){
if(d == null || d < 0.0) return null;
String s = d.toString();
int n = s.length() - s.lastIndexOf('.');
if(n > 3) return null;
if(n == 2) s += "0";
String[] sa = s.split("\\.");
threeChars = new Stack <ThreeChar> ();
threeChars.push(new ThreeChar());
threeChars.peek().range = Ranges.UNITS;
StringBuilder sb = new StringBuilder(sa[0]).reverse();
for(int i = 0; i < sb.length(); i++){
if(i > 0 && i % 3 == 0){
threeChars.push(new ThreeChar());
}
ThreeChar threeChar = threeChars.peek();
switch(i){
case 0:
threeChar.u = sb.charAt(i);
break;
case 3:
threeChar.range = Ranges.THOUSANDS;
threeChar.u = sb.charAt(i);
break;
case 6:
threeChar.range = Ranges.MILLIONS;
threeChar.u = sb.charAt(i);
break;
case 9:
threeChar.range = Ranges.BILLIONS;
threeChar.u = sb.charAt(i);
break;
case 2:
case 5:
case 8:
threeChar.h = sb.charAt(i);
break;
default:
threeChar.d = sb.charAt(i);
}
}
StringBuilder result = new StringBuilder();
while(!threeChars.isEmpty()){
ThreeChar thch = threeChars.pop();
if(thch.h > 0 ){
result.append(getHundreds(thch.h));
result.append(' ');
}
if(thch.d > '0'){
if(thch.d > '1' || (thch.d == '1' && thch.u == '0')) result.append(getDecades(thch.d));
else if(thch.d > '0') result.append(getTeens(thch.d));
result.append(' ');
}
if(thch.u > '0' && thch.d != '1'){
result.append(getUnits(thch.u, thch.range == Ranges.THOUSANDS));
result.append(' ');
}
switch(thch.range){
case BILLIONS:
if(thch.d == '1' || thch.u == '0') result.append("миллиардов");
else if(thch.u > '4')result.append("миллиардов");
else if(thch.u > '1')result.append("миллиарда");
else result.append("миллиард");
break;
case MILLIONS:
if(thch.d == '1' || thch.u == '0') result.append("миллионов");
else if(thch.u > '4')result.append("миллионов");
else if(thch.u > '1')result.append("миллиона");
else result.append("миллион");
break;
case THOUSANDS:
if(thch.d == '1' || thch.u == '0') result.append("тысяч");
else if(thch.u > '4')result.append("тысяч");
else if(thch.u > '1')result.append("тысячи");
else result.append("тысяча");
break;
default:
if(thch.d == '1' || thch.u == '0' || thch.u > '4')result.append("рублей");
else if(thch.u > '1')result.append("рубля");
else result.append("рубль");
}
result.append(' ');
}
result.append(sa[1] + ' ');
switch(sa[1].charAt(1)){
case '1':
result.append(sa[1].charAt(0) != '1' ? "копейка" : "копеек");
break;
case '2':
case '3':
case '4':
result.append(sa[1].charAt(0) != '1' ? "копейки" : "копеек");
break;
default:
result.append("копеек");
}
char first = Character.toUpperCase(result.charAt(0));
result.setCharAt(0, first);
return result.toString();
}
private static String getHundreds(char dig){
switch(dig){
case '1':
return "сто";
case '2':
return "двести";
case '3':
return "триста";
case '4':
return "четыреста";
case '5':
return "пятьсот";
case '6':
return "шестсот";
case '7':
return "семсот";
case '8':
return "восемсот";
case '9':
return "девятьсот";
default: return null;
}
}
private static String getDecades(char dig){
switch(dig){
case '1':
return "десять";
case '2':
return "двадцать";
case '3':
return "тридцать";
case '4':
return "сорок";
case '5':
return "пятьдесят";
case '6':
return "шестьдесят";
case '7':
return "семьдесят";
case '8':
return "восемьдесят";
case '9':
return "девяносто";
default: return null;
}
}
private static String getUnits(char dig, boolean female){
switch(dig){
case '1':
return female ? "одна" : "один";
case '2':
return female ? "две" : "два";
case '3':
return "три";
case '4':
return "четыре";
case '5':
return "пять";
case '6':
return "шесть";
case '7':
return "семь";
case '8':
return "восемь";
case '9':
return "девять";
default: return null;
}
}
private static String getTeens(char dig){
String s = "";
switch(dig){
case '1':
s = "один"; break;
case '2':
s = "две"; break;
case '3':
s = "три"; break;
case '4':
s = "четыр"; break;
case '5':
s = "пят"; break;
case '6':
s = "шест"; break;
case '7':
s = "сем"; break;
case '8':
s = "восем"; break;
case '9':
s = "девят"; break;
}
return s + "надцать";
}
public static void main(String[] args){
System.out.println(new RussianMoney().digits2Text(new Double(args[0])));
}
}
5
Author: Cobalt, 2015-09-08 09:19:54
On geektimes there is a version of the finished implementation:
import java.util.ArrayList;
import java.util.Collections;
import java.math.BigDecimal;
/**
* Класс для работы с деньгами
* @author runcore
*/
public class fwMoney {
/**
* Сумма денег
*/
private BigDecimal amount;
/**
* Конструктор из Long
*/
public fwMoney(long l) {
String s = String.valueOf(l);
if (!s.contains(".") )
s += ".0";
this.amount = new BigDecimal( s );
}
/**
* Конструктор из Double
*/
public fwMoney(double l) {
String s = String.valueOf(l);
if (!s.contains(".") )
s += ".0";
this.amount = new BigDecimal( s );
}
/**
* Конструктор из String
*/
public fwMoney(String s) {
if (!s.contains(".") )
s += ".0";
this.amount = new BigDecimal( s );
}
/**
* Вернуть сумму как строку
*/
public String asString() {
return amount.toString();
}
/**
* Вернуть сумму прописью, с точностью до копеек
*/
public String num2str() {
return num2str(false);
}
/**
* Выводим сумму прописью
* @param stripkop boolean флаг - показывать копейки или нет
* @return String Сумма прописью
*/
public String num2str(boolean stripkop) {
String[][] sex = {
{"","один","два","три","четыре","пять","шесть","семь","восемь","девять"},
{"","одна","две","три","четыре","пять","шесть","семь","восемь","девять"},
};
String[] str100= {"","сто","двести","триста","четыреста","пятьсот","шестьсот","семьсот", "восемьсот","девятьсот"};
String[] str11 = {"","десять","одиннадцать","двенадцать","тринадцать","четырнадцать", "пятнадцать","шестнадцать","семнадцать","восемнадцать","девятнадцать","двадцать"};
String[] str10 = {"","десять","двадцать","тридцать","сорок","пятьдесят","шестьдесят", "семьдесят","восемьдесят","девяносто"};
String[][] forms = {
{"копейка", "копейки", "копеек", "1"},
{"рубль", "рубля", "рублей", "0"},
{"тысяча", "тысячи", "тысяч", "1"},
{"миллион", "миллиона", "миллионов", "0"},
{"миллиард","миллиарда","миллиардов","0"},
{"триллион","триллиона","триллионов","0"},
// можно добавлять дальше секстиллионы и т.д.
};
// получаем отдельно рубли и копейки
long rub = amount.longValue();
String[] moi = amount.toString().split("\\.");
long kop = Long.valueOf(moi[1]);
if (!moi[1].substring( 0,1).equals("0") ){// начинается не с нуля
if (kop<10 )
kop *=10;
}
String kops = String.valueOf(kop);
if (kops.length()==1 )
kops = "0"+kops;
long rub_tmp = rub;
// Разбиватель суммы на сегменты по 3 цифры с конца
ArrayList segments = new ArrayList();
while(rub_tmp>999) {
long seg = rub_tmp/1000;
segments.add( rub_tmp-(seg*1000) );
rub_tmp=seg;
}
segments.add( rub_tmp );
Collections.reverse(segments);
// Анализируем сегменты
String o = "";
if (rub== 0) {// если Ноль
o = "ноль "+morph( 0, forms[1][ 0],forms[1][1],forms[1][2]);
if (stripkop)
return o;
else
return o +" "+kop+" "+morph(kop,forms[ 0][ 0],forms[ 0][1],forms[ 0][2]);
}
// Больше нуля
int lev = segments.size();
for (int i= 0; i<segments.size(); i++ ) {// перебираем сегменты
int sexi = (int)Integer.valueOf( forms[lev][3].toString() );// определяем род
int ri = (int)Integer.valueOf( segments.get(i).toString() );// текущий сегмент
if (ri== 0 && lev>1) {// если сегмент ==0 И не последний уровень(там Units)
lev--;
continue;
}
String rs = String.valueOf(ri); // число в строку
// нормализация
if (rs.length()==1) rs = "00"+rs;// два нулика в префикс?
if (rs.length()==2) rs = "0"+rs; // или лучше один?
// получаем циферки для анализа
int r1 = (int)Integer.valueOf( rs.substring( 0,1) ); //первая цифра
int r2 = (int)Integer.valueOf( rs.substring(1,2) ); //вторая
int r3 = (int)Integer.valueOf( rs.substring(2,3) ); //третья
int r22= (int)Integer.valueOf( rs.substring(1,3) ); //вторая и третья
// Супер-нано-анализатор циферок
if (ri>99) o += str100[r1]+" "; // Сотни
if (r22>20) {// >20
o += str10[r2]+" ";
o += sex[ sexi ][r3]+" ";
}
else { // <=20
if (r22>9) o += str11[r22-9]+" "; // 10-20
else o += sex[ sexi ][r3]+" "; // 0-9
}
// Единицы измерения (рубли...)
o += morph(ri, forms[lev][ 0],forms[lev][1],forms[lev][2])+" ";
lev--;
}
// Копейки в цифровом виде
if (stripkop) {
o = o.replaceAll(" {2,}", " ");
}
else {
o = o+""+kops+" "+morph(kop,forms[ 0][ 0],forms[ 0][1],forms[ 0][2]);
o = o.replaceAll(" {2,}", " ");
}
return o;
}
/**
* Склоняем словоформу
* @param n Long количество объектов
* @param f1 String вариант словоформы для одного объекта
* @param f2 String вариант словоформы для двух объектов
* @param f5 String вариант словоформы для пяти объектов
* @return String правильный вариант словоформы для указанного количества объектов
*/
public static String morph(long n, String f1, String f2, String f5) {
n = Math.abs(n) % 100;
long n1 = n % 10;
if (n > 10 && n < 20) return f5;
if (n1 > 1 && n1 < 5) return f2;
if (n1 == 1) return f1;
return f5;
}
}
Usage Example:
fwMoney mo = new fwMoney(«7654321.98»);
String money_as_string = mo.num2str();
Result: "seven million six hundred and fifty-four thousand three hundred and twenty-one rubles 98 kopecks"
2
Author: JtHermit, 2015-09-08 12:13:22
I also trained and did it my own way
import java.util.Scanner;
public class NumberToText {
// Перевод числа в диапазоне -999 - 999 в текстовую форму
private static int billion;
private static int million;
private static int thousand;
private static int toThousand;
private static long numberA;
private static long numberMax = 999999999999L ;
// private int numericalValue;
private static String numText;// число в виде текста
//private int index ;
private static int indexA;
private static int units; // единичные значение
private static int decimal; // десятичное значение
private static int hundreds; // сотни
private static final String[][]sampleText ={ {"","од","дв","три","четыре","пять","шесть","семь","восемь","девять"},
{"", "десять " ,"двадцать ","тридцать ","сорок ","пятьдесят ","шестьдесят ","семьдесят ","восемьдесят ","девяносто "},
{"","сто ","двести ","триста ","четыреста ","пятьсот ","шестьсот ","семьсот ","восемьсот ","девятьсот "} };
private static final String[]sample11to19 = { "десять ", "одинадцать ", "двенадцать ", "тринадцать ", "четырнадцать ","пятнадцать ",
"шеснадцать ", "семьнадцать ", "восемьнадцать ", "девятнадцать ", "девятнадцать "} ;
private static final String[][] textMillion={{"","","",""},
{"миллиардов ","миллионов ","тысячь ",""},
{"миллиард ","миллион ","тысяча ",""},
{"миллиарда ","миллиона ","тысячи ",""},
{"миллиардов ","миллионов ","тысячь ",""}};
public static String WordsRus (long number) {
numberA = number;
numText ="";
if (numberA < -numberMax || numberA > numberMax ) {
return numText = "Число выходит за рамки указанного диапазона";}
if (numberA == 0 ) {
return numText = "ноль ";}
if (number < 0) {numText = "минус "; numberA = -numberA;} //делаем позитивное значение number
// разбиваем число на миллиарды,миллионы,тысячи и единицы
billion = (int) ( numberA / 1000000000);
million = (int) (numberA-(billion*1000000000))/ 1000000 ;
thousand = (int) (numberA - (billion*1000000000)-(million*1000000)) / 1000;
toThousand = (int)(numberA % 1000) ;
// формируем текст числа прописью
numText =numText + WordsToThousand (billion , 0)+WordsToThousand (million , 1)+WordsToThousand (thousand , 2)+WordsToThousand (toThousand , 3);
return numText ;
}
private static String WordsToThousand ( int numericalValue , int index ){
//this.numericalValue = numericalValue;
//this.index = index;
// разбиваем образец числа на составляющие
hundreds = numericalValue / 100;
decimal = (numericalValue - (hundreds*100) ) / 10;
units = numericalValue % 10 ;
// формируем число без степени числа
numText = "";
if ( decimal == 1 ) numText = sampleText [2] [hundreds] + sample11to19 [units];
else numText = sampleText [2] [hundreds] + sampleText [1][decimal] + sampleText [0] [units];
// формируем окончания в единицах
if (index == 2) {if (units == 1 && decimal != 1) numText = numText + "на ";
else if (units == 2 & decimal != 1) numText = numText + "е ";
if (units > 1 && decimal != 1) numText = numText + " ";}
else {if (units == 1 && decimal != 1) numText = numText + "ин ";
if (units == 2 & decimal != 1) {numText = numText + "а ";}
else if (units != 0 & decimal != 1) numText = numText + " ";}
// дописываем степень числа
indexA = 0;
if (numericalValue != 0 ) {
if (units == 0 || decimal == 1 ) indexA = 1;
else if (units == 1) indexA = 2;
else if (units > 1 & units < 5) indexA = 3;
else indexA = 4;}
numText = numText + textMillion [indexA][index];
return numText;
}
}
public class Dialog {
public static void main(String[] args) {
long number;// введенное число
Scanner in = new Scanner(System.in);
do {
System.out.print("Введите целое число в диапазоне -999 999 999 999 до 999 999 999 999, для выхода введите 0: ");
number = (long) in.nextDouble();
System.out.println(NumberToText.WordsRus(number));
} while ( number != 0);
System.out.println("Введено число /Ноль/ Работа программы завершена");
}
}
0
Author: Александр Блажейко, 2018-07-05 12:54:36