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?

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