CodeLAB
на главную карта сайта обратная связь

Популярные задачи:

#Заполнение 2-го выпадающего списка (select) в соответствии с выбором в первом. (47420 hits)
#Поразрядная сортировка, общий принцип. (133320 hits)
#Двусторонняя карта. (35091 hits)
#Программное создание ссылок. (101062 hits)
#Рисование куба. (61112 hits)
#Хранение иерархических деревьев. (54300 hits)
#Сглаживание кривой В-сплайном. (39896 hits)
#Сортировка Шелла, обший принцип. (147806 hits)
#Сапер. (54723 hits)
#Наибольший общий делитель. (195673 hits)
#Таймер. (41780 hits)
#Преобразование RGB в HEX и обратно HEX в RGB. (58032 hits)
#Логирование в GUI. (33442 hits)
#Сортировка вставкой. (114109 hits)
#Числа Армстронга. (47431 hits)
#Вычисление среднего, среднего отклонения, среднеквадратического отклонения и дисперсии заданной выборки. (47559 hits)
#Предварительная загрузка изображений. (48334 hits)
#Бинарный поиск в массиве и его разновидности. (174987 hits)
#Обработка шаблонных писем. (57950 hits)
#Вычисление значения полинома. (63635 hits)


Главная >> Каталог задач >> Числа >>

Преобразование сумм из цифрового представления в строковое

Aвтор:
Дата:
Просмотров: 179121
реализации(javascript: 2шт...) +добавить

Постановка задачи

fplab(с)

Предлагаемая вниманию уважаемых читателей статья посвящена одной сугубо практической задаче, которая сплошь и рядом встречается в обширных классах офисных и бизнес-программ. Прежде всего, позвольте задать вам такой вопрос: случалось ли вам видеть или более того, держать в руках какой-либо денежный документ (например, платежное поручение, кассовый чек или счет-фактуру)? И еще: обращали ли вы внимание на то, что на любой денежной купюре ее номинал указан дважды: в виде последовательности цифр (числовой номинал) и в виде той же суммы прописью? В практике разработки программного обеспечения достаточно часто возникает необходимость преобразования сумм или чисел из цифровой формы в строковое представление. Поясним это на нескольких примерах:

  • в соответствии с правилами бухгалтерского учета (принятым, не только в нашей стране, но и в международной практике), все суммы, указываемые в денежных документах должны быть отражены дважды: один раз цифрами, второй раз - прописью
  • при оформлении операций покупки/продажи иностранной валюты в обменных пунктах коммерческих банков, их клиентам на руки выдается справка для предъявления в органах таможни, в которой все необходимые суммы в рублях или иностранной валюте указываются дважды: цифрами и прописью
  • при заключении договоров и контрактов требования законодательства и существующая юридическая практика требуют, чтобы все встречающиеся в их тексте цифры (суммы, проценты, количества и проч.) были указаны дважды: цифрами и прописью
  • любая складская программа фигурирует такими понятиями как метры, килограммы, упаковки, ящики, штуки. При документальном оформлении этих товаров или их инвентаризации, мы опять сталкиваемся с необходимостью указания "двойственного" представления всех этих категорий: и цифрами, и прописью

Читатель может с легкостью продолжить этот список. Приведенные выше примеры свидетельствуют, что задача преобразования сумм цифрами в соответствующие строковые представления, достаточно широко распространена и вполне естественно желание эту задачу автоматизировать.
В Internet можно найти немало примеров такого рода программ (их часто называют "конверторами"). Большая часть из них написана на VB или VBA и предназначена для выполнения соответствующих преобразований в приложениях MS Office. Немало существует конверторов для FoxPro. Наша задача заключается в написании Java-приложения, осуществляющего конвертацию сумм из цифрового представления, в строковое. Для определенности, мы напишем приложение, которое будет конвертировать денежные суммы, как наиболее часто встречающийся случай. Внимательный читатель с легкостью сумеет распространить "сферу влияния" этого приложения и на другие категории чисел которые нужно конвертировать в строки.

Прежде чем мы начнем говорить о самом приложении, нам придется на некоторое время "углубиться" в тонкости грамматики. Необходимость этого станет понятна, если мы рассмотрим небольшой пример:

810 121.21 Сто двадцать один рубль 21 копейка
810 121.23 Сто двадцать один рубль 23 копейки
810 121.25 Сто двадцать один рубль 25 копеек

Небольшое пояснение к структуре этой (и следующей) таблицы: первая колонка представляет собой международный числовой код валюты (в данном случае, рублей), вторая - сумма в цифровом выражении и, наконец, третья - строковое представление суммы из второй колонки с учетом написания заданной валюты.
Обратите внимание на выделенные фрагменты. Очевидно, что не только падеж, но и число (единственное или множественное) в третьей колонке таблицы зависят от цифрового значения копеек в ее второй колонке. Теперь немного изменим исходные данные:

 

810 121.21 Сто двадцать один рубль 21 копейка
276 121.21 Сто двадцать одна марка 21 пфенниг
840 121.21 Сто двадцать один доллар 21 цент

 

А здесь уже что-то новенькое: оказывается, нам необходимо учитывать не только падежи и числа (единственное или множественное), но и род той или иной денежной единицы. В данном случае марка (код 276) - валюта женского рода, а рубль (810) и доллар (840) - мужского.
Таким образом, как в рекламе зубной щетки: "в общем, вы поняли ...". Потратьте несколько минут на преобразования различных сумм в разных валютах в строки и вы согласитесь, что при всей простоте постановки задачи, ее решение, отнюдь не очевидно.
Однако, поставленная задача актуальна и ее необходимо как то решить, т.е. написать программу на выбранном нами языке программирования Java. Поэтому мы, как тому учат классики программирования (Ч.Хоар, Н.Вирт, Э.Дейкстра и Д.Кнут), займемся выработкой некой абстракции, которая позволит нам учесть все возможные варианты написания сумм в строки.

Строим абстракцию

Ограниченные рамки on-line публикации не позволят нам, к сожалению, изложить детальное описание процесса выработки необходимой нам абстракции и, поэтому, мы приводим только итоговую таблицу для одной валюты (код 810, т.е. рубли):

 

Последняя цифра Целая часть (слева) Дробная часть (справа) Род
1 рубль копейка Мужской
2, 3 ,4 рубля копейки Мужской
0, 5 рублей копеек Мужской

 

Внимательно рассмотрите эту таблицу. Попробуйте с ее помощью конвертировать какую либу сумму (в рублях, разумеется) в соответствующее строковое представление. Забавно, не правда ли ? Задержитесь на несколько минут и постройте подобные таблицы для марок (код 276), долларов (код 840) или любой другой валюты.
Не кажется ли вам, что теперь перед нами забрезжил выход из этой весьма запутанной ситуации с падежами, родами и прочими грамматическими "премудростями" ? Теперь становится понятно, как можно "справиться" с имеющейся сложностью: достаточно где-то (лучше всего в какой-либо базе данных или в конфигурационном файле) хранить приведенную таблицу для каждой нужной валюты и написать некий "обработчик" этой информации.
Конечно, этот обработчик будет, по видимости включать в себя большое количество инструкций выбора (т.е. конструкций if и case) и трудно рассчитывать на то, что он будет компактным, однако принципиально, он (обработчик) не сложен: берем число, разбиваем его на группы и подбираем для каждой группы соответствующее строковое представление из базы данных или файла конфигурации. Поэтому, приступим к программированию, но перед этим сначала решим...

Где хранить настроечную информацию?

Сразу договоримся, что вышеприведенную таблицу мы будем в дальнейшем называть "настроечной информацией". Мы уже говорили, что настроечную информацию по валютам можно хранить либо в базе данных, либо в конфигурационном файле. Мы остановились на первом способ, поскольку, как правило, бухгалтерские приложения уже включают в себя справочник валют в виде одной или нескольких таблиц базы данных. В качестве базы данных мы выбираем широко распространненный сервер MySQL. Кроме, собственно, сервера нам потребуется еще jdbc-драйвер. Если вы никогда не работали с MySQL или с этим драйвером, ничего страшного: оба они довольно просто инсталлируются и снабжены удобной и понятной документацией.
Более того, принятый нами подход отличается замечательной особенностью: его можно использовать практически с любой базой данных; главное условие - наличие соответствующего jdbc-драйвера. Тем не менее, приведенные примеры кода ориентированы именно на MySQL и, возможно, вам придется их немного изменить. Впрочем, как показывает практика, это не составляет большого труда.
Наше приложение будет состоять из двух основных классов: fplAmount и jAmount. Первый класс носит вспомогательный характер и предназначен для создания тестовой базы данных, таблицы валют и заполнения этой таблицы необходимой настроечной информацией. Кроме того, этот класс предоставлет пользователю простую графическую оболочку для выбора валюты и ввода суммы цифрами. Второй класс осуществляет преобразование введенной суммы из цифрового представления в строковое. Рассмотрим сначала класс fplAmount.
Начнем, как водится, с метода main (точка входа в приложение):

 точка входа, структура приложения [java]  ссылка
  1. // Импорт необходимых пакетов JDK 1.3
  2. import java.awt.*;
  3. import java.awt.event.*;
  4. import java.sql.*;
  5. import java.util.*;
  6. import javax.swing.*;
  7. import javax.swing.event.*;
  8.  
  9. public class fplAmount extends JFrame {
  10.  
  11. static JFrame frame; // Основное окно приложения
  12. static JComboBox currBox; // Выпадающий список валют
  13. static JTextField summaField; // Поле для ввода суммы
  14. static Connection conn = null; // Объект-соединение
  15. static Vector cv = new Vector (); // Вектор кодов валют
  16.  
  17. // В этой строке будет находиться результат преобразования
  18. // суммы цифрами в сумму прописью
  19. static String res = "";
  20. ...........................
  21. // Точка входа ...
  22. public static void main (String [] args) throws Exception {
  23. connect (); // Подсоединиться к серверу MySQL
  24. checkAndFill (); // Проверить данные
  25. selectCurrency (); // Сформировать список валют
  26. UIManager.setLookAndFeel ("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
  27. frame = new fplAmount ();
  28. }
  29. }

Здесь же мы привели объявления некоторых переменных, которые потребуются нам в этом приложении. Из приведенного кода видно, что сначала мы пытаемся подсоединиться к серверу MySQL, затем проверяем и заполняем (при необходимости) данными таблицу валют, формируем список валют и, наконец, создаем и выводим на экран основное окно приложения. Для тех, кто работает в среде, отличной от Windows, можно порекомендовать закомментировать строку, определяющую стиль пользовательского интерфейса (UIManager.setLookAndFeel ...). Разберем по порядку перечисленные методы.

 подключение к mysql [java]  ссылка
  1. static void connect () {
  2. try {
  3. Class.forName ("org.gjt.mm.mysql.Driver");
  4. conn = DriverManager.getConnection (
  5. "jdbc:mysql://localhost:3306/mysql");
  6. }
  7. catch (SQLException sqle) {
  8. System.out.println ("SQL exception:" + sqle.getMessage ());
  9. System.out.println ("SQLState:" + sqle.getSQLState ());
  10. System.out.println ("VendorError:" + sqle.getErrorCode ());
  11. System.exit (-1);
  12. }
  13. catch (Exception sqle) {sqle.printStackTrace ();}
  14. }

Сначала мы загружаем класс-драйвер, а затем пытаемся получить объект- соединение с сервером MySQL. Поскольку мы не знаем, существует ли на момент создания соединения с сервером MySQL необходимая нам база данных, мы "обманываем" сервер, подсоединяясь к наверняка существующей базе данных mysql (заметим, что для этого у нас должны быть соответствующие права доступа, поэтому, не исключено, что вам придется изменить параметр в методе DriverManager.getConnection и добавить в него имя и пароль). Если соединение установлено успешно, то объект-соединение Connection conn получает значение, отличное от null. Если же соединение установить не удалось (например потому, что сервер MySQL не запущен или в параметре метода DriverManager.getConnection что-то не так), то приложение аварийно завершит свою работу и выйдет в операционную среду.

Теперь рассмотрим метод checkAndFill():

 checkAndFill () [java]  ссылка
  1. // Проверить существование таблицы валют и заполнить ее данными
  2. static void checkAndFill () {
  3. Statement stmt = null;
  4. // SQL-запрос для создания тестовой базы данных
  5. String sqlCreateDB = "create database jAmount";
  6. String sqlUseDB = "use jAmount";
  7. // SQL-запрос для создания тестовой таблицы валют
  8. String sqlCreateTable = "create table currency (" +
  9. "ID_Currency smallint not null," +
  10. "ISO_Currency char (3)," +
  11. "Scale int," +
  12. "Description char (32)," +
  13. "i1 varchar (32)," +
  14. "i24 varchar (32)," +
  15. "i5 varchar (32)," +
  16. "r1 varchar (32)," +
  17. "r24 varchar (32)," +
  18. "r5 varchar (32)," +
  19. "Sex char (1)," +
  20. "primary key (ID_Currency))";
  21. // SQL-запросы для заполнения таблицы валют тестовыми данными
  22. String [] sqlText = {
  23. "insert into currency values (810,'RUR',1,'Российские рубли','рубль','рубля','рублей','копейка','копейки','копеек','M')",
  24. "insert into currency values (276,'DEM',1,'Немецкие марки','марка','марки','марок','пфенниг','пфеннига','пфеннигов','F')",
  25. "insert into currency values (840,'USD',1,'Доллары США','доллар','доллара','долларов','цент','цента','центов','M')"
  26. };
  27.  
  28. // Создать базу данных и выбрать ее по умолчанию
  29. try {
  30. stmt = conn.createStatement ();
  31. boolean value = stmt.execute (sqlCreateDB);
  32. }
  33. catch (SQLException sqle) {}
  34. catch (Exception sqle) {}
  35. try {
  36. stmt = conn.createStatement ();
  37. boolean value = stmt.execute (sqlUseDB);
  38. }
  39. catch (SQLException sqle) {}
  40. catch (Exception sqle) {}
  41.  
  42. // Cоздать таблицу со списком валют
  43. try {
  44. stmt = conn.createStatement ();
  45. boolean value = stmt.execute (sqlCreateTable);
  46. }
  47. catch (SQLException sqle) {}
  48. catch (Exception sqle) {}
  49. // Заполнить тестовую таблицу валют данными
  50. for (int i = 0; i <= sqlText.length; i++) {
  51. try {
  52. int value = stmt.executeUpdate (sqlText [i]);
  53. }
  54. catch (SQLException sqle) {}
  55. catch (Exception sqle) {}
  56. }
  57. }

Этот метод довольно прост и прямолинеен. Сначала создаются строковые переменные для создания базы данных (sqlCreateDB), выбора базы данных по умолчанию (sqlUseDB), создания таблицы валют (sqlCreateTable) и, наконец, массив строк для заполнения таблицы валют данными (sqlText). Далее последовательно выполняются необходимые SQL-инструкции.
Остановимся несколько подробней на переменной sqlCreateTable. Как видно из приведенного кода структура таблицы валют проста и (за исключением нескольких полей) полностью дублирует структуру таблицы из раздела "Строим абстракцию". Поскольку структура этой таблицы будет играть ключевую роль в дальнейшем изложении, мы еще раз рассмотрим ее:

Поле Тип Назначение
ID_Currency smallint числовой идентификатор валюты (810, 276, 840 и т.д.)
ISO_Currency char (3) так называемый ISO-код (буквенное обозначение валюты: (рубли - RUR, доллары - USD, марки - 276 и т.д.)
Scale int масштаб (т.е. за какое количество данной валюты устанавливаются котрировки Банка России
Description char (32) наименование валюты
i1 varchar (32) строковое представление последней цифры целой части
i24 varchar (32) строковое представление последней цифры целой части
i5 varchar (32) строковое представление последней цифры целой части
r1 varchar (32) строковое представление последней цифры дробной части
r24 varchar (32) строковое представление последней цифры дробной части
r5 varchar (32) строковое представление последней цифры дробной части
Sex char (1) род валюты (M - мужской, F - женский)

 

Наконец, в sqlText указаны данные для трех валют: рублей, марок и долларов. Т.о. мы создаем базу данных, выбираем ее по умолчанию, создаем таблицу валют и заполняем ее в цикле данными из массива sqlText.

Последний метод, который мы должны рассмотреть, это метод selectCurrency(). Его назначение понятно: выбрать числовые коды (ID_Currency) в вектор.

 selectCurrency() [java]  ссылка
  1. // Выбрать коды валют из таблицы CURRENCY в вектор
  2. static void selectCurrency () {
  3. try {
  4. Statement stmt = conn.createStatement ();
  5. ResultSet rset = stmt.executeQuery (
  6. "select id_currency from currency");
  7. while (rset.next ()) {
  8. cv.addElement (rset.getObject (1));
  9. }
  10. }
  11. catch (SQLException sqle) {
  12. System.out.println ("SQL exception:" + sqle.getMessage ());
  13. System.out.println ("SQLState:" + sqle.getSQLState ());
  14. System.out.println ("VendorError:" + sqle.getErrorCode ());
  15. System.exit (-1);
  16. }
  17. catch (Exception sqle) {sqle.printStackTrace ();}
  18. }

Мы оставляем этот метод без комментариев, поскольку он очень прост.

Наконец, чтобы окончательно закончить подготовку к конвертации сумм в строковое представление, представим конструктор, который создает основное окно приложения и позволяет организовать диалог с пользователем:

 основное окно приложения [java]  ссылка
  1. // Создаем основное окно приложения
  2. public fplAmount () {
  3. setTitle ("Сумма прописью");
  4. setFont (new Font ("Helvetica", Font.PLAIN, 12));
  5. // Разместить компоненты в основном окне
  6. JLabel currLabel = new JLabel (" Валюта ", JLabel.RIGHT),
  7. summaLabel = new JLabel (" Сумма ", JLabel.RIGHT);
  8. currBox = new JComboBox (cv);
  9. summaField = new JTextField ("0.00", 12);
  10. JButton convertButton = new JButton ("Преобразовать");
  11. JPanel lPanel = new JPanel (new GridLayout (2, 1)),
  12. rPanel = new JPanel (new GridLayout (2, 1)),
  13. topPanel = new JPanel (new BorderLayout ()),
  14. botPanel = new JPanel (new FlowLayout (FlowLayout.RIGHT));
  15. lPanel.add (currLabel);
  16. lPanel.add (summaLabel);
  17. rPanel.add (currBox);
  18. rPanel.add (summaField);
  19. topPanel.add (lPanel, BorderLayout.WEST);
  20. topPanel.add (rPanel, BorderLayout.CENTER);
  21. botPanel.add (convertButton);
  22. getContentPane ().add (topPanel, BorderLayout.CENTER);
  23. getContentPane ().add (botPanel, BorderLayout.SOUTH);
  24. pack ();
  25. setSize (getPreferredSize ().width, getPreferredSize ().height);
  26. setResizable (true);
  27. // Вывести основное окно в центре экрана монитора
  28. Dimension sSize = Toolkit.getDefaultToolkit ().getScreenSize ();
  29. Dimension fSize = getSize ();
  30. if (fSize.height > sSize.height) fSize.height = sSize.height;
  31. if (fSize.width > sSize.width) fSize.width = sSize.width;
  32. setLocation ((sSize.width - fSize.width)/2, (sSize.height - fSize.height)/2);
  33. setVisible (true);
  34. // Обработчик события нажатия на кнопку "Преобразовать"
  35. convertButton.addActionListener (new ActionListener () {
  36. public void actionPerformed (ActionEvent e) {
  37. // Определить код выбранной валюты из выпадающего списка
  38. String code = currBox.getSelectedItem ().toString ();
  39. // Массив суффиксов - окончаний. Элементы массива:
  40. // 0...5 - строки целой и дробной частей;
  41. // 6 - род валюты (M - мужской, F - женский)
  42. String [] suff = {"", "", "", "", "", "", ""};
  43. // Выбрать из таблицы настроечную информацию,
  44. // касающуюся данной валюты
  45. try {
  46. Statement stmt = conn.createStatement ();
  47. ResultSet rset = stmt.executeQuery ("select i1,i24,i5,r1,r24,r5,Sex from currency where id_currency=" + code);
  48. ResultSetMetaData meta = rset.getMetaData ();
  49. int cols = meta.getColumnCount ();
  50. // Занести настроечную информацию в массив
  51. while (rset.next ()) {
  52. for (int i = 0; i < cols; i++) suff [i] = rset.getString (i + 1);
  53. }
  54. }
  55. catch (SQLException sqle) {
  56. System.out.println ("SQL exception:" + sqle.getMessage ());
  57. System.out.println ("SQLState:" + sqle.getSQLState ());
  58. System.out.println ("VendorError:" + sqle.getErrorCode ());
  59. System.exit (-1);
  60. }
  61. catch (Exception sqle) {sqle.printStackTrace ();}
  62. // Перевести число в строку !!!
  63. new jAmount (suff, summaField.getText ());
  64. }
  65. });
  66.  
  67. addWindowListener (new WindowAdapter () {
  68. public void windowClosing (WindowEvent event) {
  69. System.exit (0);
  70. }
  71. });
  72. }

Единственный заслуживающий пристального внимания момент, заключается в том, что после нажатия пользоватлем кнопки "Преобразовать" мы должны обратиться к таблице валют и выбрать из нее в массив suff настроечную информацию для выбранной валюты. Этот массив (вместе со строкой, представляющей собой сумму) передаются классу jAmount для преобразования (конвертирования) суммы цифрами в строку.

Конвертируем

Вот наконец-то, мы добрались до цели нашего рассказа: преобразования сумм цифрами в строку. Класс jAmount достаточно объемен, однако, он принципиально не сложен, поскольку всю подготовительную работу мы выполнили ранее. Напомним, что нашей целью было получение т.н. настроечной информации по выбранной валюте. Эту настроечную информацию (в виде массива) мы и передаем конструктору класса jAmount. И еще: кроме собственно настроечной информации мы передаем строку, представляющую собой набор цифр, который класс jAmount, должен преобразовать (конвертировать) в строковое представление суммы. Мы не будем комментировать этот класс и его методы так же, как мы комментировали класс fplAmount. Все заслуживающие внимания моменты содержатся в комментариях к приведенному коду. Не пугайтесь, если сразу вам не удастся понять суть применяемого алгоритма. Скопируйте код на свой диск, вставьте в "подозрительные" или непонятные места отладочную печать на консоль или лучше в файл, откомпилируйте и изучайте !

 конвертация [java]  ссылка
  1. import java.math.*;
  2.  
  3. public class jAmount {
  4.  
  5. private BigInteger summ;
  6. private String [] suff;
  7. private static final BigInteger zero = new BigInteger ("0");
  8. private static final BigInteger hundred = new BigInteger ("100");
  9. private static final BigInteger thousand = new BigInteger ("1000");
  10.  
  11. // Конструктор класса. Конструктор в качестве параметров получает:
  12. // String suff [] массив наименований (настроечная информация) и
  13. // String sum сумма для преобразования
  14. public jAmount (String [] suff, String s) {
  15. this.suff = suff;
  16. try {
  17. BigDecimal decimal = new BigDecimal (s);
  18. // Преобразуем в копейки (центы, пфенниги и т.д.),
  19. // одним словом - убираем дробную часть
  20. decimal = decimal.multiply (new BigDecimal (100.00));
  21. summ = decimal.toBigInteger ();
  22. // Приступить к преобразованию
  23. toString ();
  24. // Метод для вывода результата преобразования. Можно просто
  25. // выводить полученное строковое представление суммы на консоль:
  26. // System.out.println (fplAmount.res);
  27. jAmountResult jar = new jAmountResult (fplAmount.frame);
  28. jar.setVisible (true);
  29. }
  30. // Ой !!!!! Что-то не так: скорее всего, в строке
  31. // представляющей собой сумму цифрами, встретились символы
  32. // отличные от цифр и точки. Можно просто выводить сообщение
  33. // об ошибках на консоль:
  34. // System.out.println (e);
  35. jAmountError jae = new jAmountError (fplAmount.frame);
  36. jae.setVisible (true);
  37. }
  38. }
  39.  
  40. // Получить правую (дробную) часть суммы
  41. public String getRightPart () {
  42. return alignSumm (summ.remainder (hundred).abs ().toString ());
  43. }
  44.  
  45. // Если сумма меньше 10, то выровнять ее дописыванием "0"
  46. String alignSumm (String s) {
  47. switch (s.length ()) {
  48. case 0: s = "00"; break;
  49. case 1: s = "0" + s; break;
  50. }
  51. return s;
  52. }

Далее следует собственно сам преобразователь суммы цифрами в строку. Прежде чем вы посмотрите реализацию этого метода, автор считает своим долгом выразить глубокую благодарность и восхищение программистам компании "Бифит", на чьем сайте была выложена реализация алгоритма преобразования (правда, только для рублей). Автор несколько переработал алгоритм, однако, основная идея осталась без изменений. Переработки были связаны исключительно с тем, что требовалось "подсунуть" алгоритму настроечную информацию по выбранной валюте, однако, повторюсь, идея и реализация принадлежат компании "Бифит" и работающим в ней программистам.

 ключевое преобразование [java]  ссылка
  1. public String toString () {
  2. StringBuffer result = new StringBuffer ();
  3. BigInteger [] divrem = summ.divideAndRemainder (hundred);
  4. if (divrem [0].signum () == 0) result.append ("Ноль ");
  5. divrem = divrem [0].divideAndRemainder (thousand);
  6. BigInteger quotient = divrem [0];
  7. BigInteger remainder = divrem [1];
  8. int group = 0;
  9. do {
  10. int value = remainder.intValue ();
  11. result.insert (0, groupToString (value, group));
  12. // Для нулевой группы добавим в конец соответствующую валюту
  13. if (group == 0) {
  14. int rank10 = (value % 100) / 10;
  15. int rank = value % 10;
  16. if (rank10 == 1) {
  17. result = result.append (suff [2]);
  18. }
  19. else {
  20. switch (rank) {
  21. case 1: result = result.append (suff [0]); break;
  22. case 2:
  23. case 3:
  24. case 4: result = result.append (suff [1]); break;
  25. default: result = result.append (suff [2]); break;
  26. }
  27. }
  28. }
  29. divrem = quotient.divideAndRemainder (thousand);
  30. quotient = divrem [0];
  31. remainder = divrem [1];
  32. group++;
  33. }
  34. while (!quotient.equals (zero) || !remainder.equals (zero));
  35.  
  36. // Дробная часть суммы
  37. String s = getRightPart ();
  38. result = result.append (" ").append (s);
  39. result = result.append (" ");
  40.  
  41. if (s.charAt (0) == '1') {
  42. result = result.append (suff [5]);
  43. }
  44. else {
  45. switch (s.charAt(1)) {
  46. case '1': result = result.append (suff [3]); break;
  47. case '2':
  48. case '3':
  49. case '4': result = result.append (suff [4]); break;
  50. default: result = result.append (suff [5]); break;
  51. }
  52. }
  53. // По правилам бухгалтерского учета первая буква строкового
  54. // представления должна быть в верхнем регистре
  55. result.setCharAt (0, Character.toUpperCase (result.charAt (0)));
  56.  
  57. // Вот ради этой строки все и затевалось: результат получен !!!
  58. fplAmount.res = result.toString();
  59. return result.toString();
  60. }
  61.  
  62. // Преобразование группы цифр в строку
  63. String groupToString (int value, int group) {
  64. if (value < 0 || value > 999) throw new IllegalArgumentException ("value must be between 0 an 999 inclusively");
  65. if (group < 0) throw new IllegalArgumentException ("group must be more than 0");
  66. StringBuffer result = new StringBuffer (32);
  67. if (value == 0) {
  68. return result.toString();
  69. }
  70. // Разбор числа по разрядам, начиная с сотен
  71. int rank = value / 100;
  72. switch (rank) {
  73. default: break;
  74. case 1: result = result.append ("сто "); break;
  75. case 2: result = result.append ("двести "); break;
  76. case 3: result = result.append ("триста "); break;
  77. case 4: result = result.append ("четыреста "); break;
  78. case 5: result = result.append ("пятьсот "); break;
  79. case 6: result = result.append ("шестьсот "); break;
  80. case 7: result = result.append ("семьсот "); break;
  81. case 8: result = result.append ("восемьсот "); break;
  82. case 9: result = result.append ("девятьсот "); break;
  83. }
  84. // Далее, десятки
  85. rank = (value % 100) / 10;
  86. switch (rank) {
  87. default: break;
  88. case 2: result = result.append ("двадцать "); break;
  89. case 3: result = result.append ("тридцать "); break;
  90. case 4: result = result.append ("сорок "); break;
  91. case 5: result = result.append ("пятьдесят "); break;
  92. case 6: result = result.append ("шестьдесят "); break;
  93. case 7: result = result.append ("семьдесят "); break;
  94. case 8: result = result.append ("восемьдесят "); break;
  95. case 9: result = result.append ("девяносто "); break;
  96. }
  97. // Если десятки = 1, добавить соответствующее значение. Иначе - единицы
  98. int rank10 = rank;
  99. rank = value % 10;
  100. if (rank10 == 1) {
  101. switch (rank) {
  102. case 0: result = result.append ("десять "); break;
  103. case 1: result = result.append ("одиннадцать "); break;
  104. case 2: result = result.append ("двенадцать "); break;
  105. case 3: result = result.append ("тринадцать "); break;
  106. case 4: result = result.append ("четырнадцать "); break;
  107. case 5: result = result.append ("пятнадцать "); break;
  108. case 6: result = result.append ("шестнадцать "); break;
  109. case 7: result = result.append ("семнадцать "); break;
  110. case 8: result = result.append ("восемнадцать "); break;
  111. case 9: result = result.append ("девятнадцать "); break;
  112. }
  113. }
  114. else {
  115. switch (rank) {
  116. default:
  117. break;
  118. case 1:
  119. if (group == 1) // Тысячи
  120. result = result.append ("одна ");
  121. else
  122. // Учесть род валюты (поле "Sex" настроечной информации)
  123. if (suff [6].equals ("M")) result = result.append ("один ");
  124. if (suff [6].equals ("F")) result = result.append ("одна ");
  125. break;
  126. case 2:
  127. if (group == 1) // Тысячи
  128. result = result.append ("две ");
  129. else
  130. // Учесть род валюты (поле "Sex" настроечной информации)
  131. if (suff [6].equals ("M")) result = result.append ("два ");
  132. if (suff [6].equals ("F")) result = result.append ("две ");
  133. break;
  134. case 3: result = result.append ("три "); break;
  135. case 4: result = result.append ("четыре "); break;
  136. case 5: result = result.append ("пять "); break;
  137. case 6: result = result.append ("шесть "); break;
  138. case 7: result = result.append ("семь "); break;
  139. case 8: result = result.append ("восемь "); break;
  140. case 9: result = result.append ("девять "); break;
  141. }
  142. }
  143. // Значение группы: тысячи, миллионы и т.д.
  144. switch (group) {
  145. default:
  146. break;
  147. case 1:
  148. if (rank10 == 1)
  149. result = result.append ("тысяч ");
  150. else {
  151. switch (rank) {
  152. default: result = result.append ("тысяч "); break;
  153. case 1: result = result.append ("тысяча "); break;
  154. case 2:
  155. case 3:
  156. case 4: result = result.append ("тысячи "); break;
  157. }
  158. }
  159. break;
  160. case 2:
  161. if (rank10 == 1)
  162. result = result.append ("миллионов ");
  163. else {
  164. switch (rank) {
  165. default: result = result.append ("миллионов "); break;
  166. case 1: result = result.append ("миллион "); break;
  167. case 2:
  168. case 3:
  169. case 4: result = result.append ("миллиона "); break;
  170. }
  171. }
  172. break;
  173. case 3:
  174. if (rank10 == 1)
  175. result = result.append ("миллиардов ");
  176. else {
  177. switch (rank) {
  178. default: result = result.append ("миллиардов "); break;
  179. case 1: result = result.append ("миллиард "); break;
  180. case 2:
  181. case 3:
  182. case 4: result = result.append ("миллиарда "); break;
  183. }
  184. }
  185. break;
  186. case 4:
  187. if (rank10 == 1)
  188. result = result.append ("триллионов ");
  189. else {
  190. switch (rank) {
  191. default: result = result.append ("триллионов "); break;
  192. case 1: result = result.append ("триллион "); break;
  193. case 2:
  194. case 3:
  195. case 4: result = result.append ("триллиона "); break;
  196. }
  197. }
  198. break;
  199. }
  200. return result.toString();
  201. }
  202. }

Вот, собственно, и все. Нам осталось только провести небольшое:

Испытание

Испытать наше приложение достаточно просто:

  • убедитесь, что сервер MySQL запущен
  • откомпилируйте приложение javac fplAmount.java
  • запустите приложение на исполнение java fplAmount

Обязательно поэкпериментируйте с самыми разными суммами для разных валют. Помните, что мы говорили об отладочной печати на консоль: если что-то осталось вам неясным или непонятным, выводите информацию на консоль и анализируйте, анализируйте, анализируйте...
В нашем изложении "за бортом" осталась реализация двух вспомогательных классов jAmountResult (для вывода результатов преобразования) и jAmountError (для вывода сообщений об ошибках). Это мы оставляем читателям в качестве несложного упражнения.

А вот теперь, действительно, все. Если у читателей возникнут вопросы, пишите на мой адрес и я постараеюсь ответить на них в самые сжатые сроки.
Кроме того, автор может предоставить совершенно бесплатно (т.е. безвозмездно) всем желающим исходные тексты приложения, которое было описано в этой статье. Для этого в поле Subject просто напишите: "jAmount (source files)".

Что дальше?


Помните, в начале статьи мы говорили о том, что не только денежные суммы должны преобразовываться в строки ? В строки необходимо переводить метры, килограммы, штуки, литры и другие единицы измерения. Рассмотренное нами приложение представляет собой отправную точку для разработки подобных задач.
Дерзайте и вам улыбнется удача !

Реализации:

javascript(2), C++(1), C#(2)   +добавить

1) Сумма прописью, альтернативная реализация на javascript, code #560[автор:-]
2) сумма прописью javascript на javascript, code #612[аноним:Евгений Гавриленко]