Преобразование числа в текст

Данный скрипт реализует преобразование чисел в текст, т.е., на примере денежной суммы, из 1256.18 получим Одна тысяча двести пятьдесят шесть рублей 18 копеек.

Класс преобзования

В этом классе всё настолько прозрачно, что он почти не загружен лишними комментариями.

Скрипт в нижней своей части содержит форму для тестовой работы, которую необходимо убрать в рабочей версии.

<?php

class NumToText
{
   var $Mant = array(); // описания мантисс
   // к примеру ('рубль', 'рубля', 'рублей')
   // или ('метр', 'метра', 'метров')
   var $Expon = array(); // описания экспонент
   // к примеру ('копейка', 'копейки', 'копеек')

   function NumToText()
   {
   }

   // установка описания мантисс
   function SetMant($mant)
   {
      $this->Mant = $mant;
   }

   // установка описания экспонент
   function SetExpon($expon)
   {
      $this->Expon = $expon;
   }

   // функция возвращает необходимый индекс описаний разряда
   // ('миллион', 'миллиона', 'миллионов') для числа $ins
   // например для 29 вернется 2 (миллионов)
   // $ins максимум два числа
   function DescrIdx($ins)
   {
      if(intval($ins/10) == 1) // числа 10 - 19: 10 миллионов, 17 миллионов
      return 2;
      else
      {
         // для остальных десятков возьмем единицу
         $tmp = $ins%10;
         if($tmp == 1) // 1: 21 миллион, 1 миллион
         return 0;
         else if($tmp >= 2 && $tmp <= 4)
         return 1; // 2-4: 62 миллиона
         else
         return 2; // 5-9 48 миллионов
      }
   }

   // IN: $in - число,
   // $raz - разряд числа - 1, 1000, 1000000 и т.д.
   // внутри функции число $in меняется
   // $ar_descr - массив описаний разряда ('миллион', 'миллиона', 'миллионов') и т.д.
   // $fem - признак женского рода разряда числа (true для тысячи)
   function DescrSot(&$in, $raz, $ar_descr, $fem = false)
   {
      $ret = '';

      $conv = intval($in / $raz);
      $in %= $raz;

      $descr = $ar_descr[ $this->DescrIdx($conv%100) ];

      if($conv >= 100)
      {
         $Sot = array('сто', 'двести', 'триста', 'четыреста', 'пятьсот',
         'шестьсот', 'семьсот', 'восемьсот', 'девятьсот');
         $ret = $Sot[intval($conv/100) - 1] . ' ';
         $conv %= 100;
      }

      if($conv >= 10)
      {
         $i = intval($conv / 10);
         if($i == 1)
         {
            $DesEd = array('десять', 'одиннадцать', 'двенадцать', 'тринадцать',
            'четырнадцать', 'пятнадцать', 'шестнадцать', 'семнадцать',
            'восемнадцать', 'девятнадцать' );
            $ret .= $DesEd[ $conv - 10 ] . ' ';
            $ret .= $descr;
            // возвращаемся здесь
            return $ret;
         }
         $Des = array('двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят',
         'семьдесят', 'восемьдесят', 'девяносто' );
         $ret .= $Des[$i - 2] . ' ';
      }

      $i = $conv % 10;
      if($i > 0)
      {
         if( $fem && ($i==1 || $i==2) )
         {
            // для женского рода (сто одна тысяча)
            $Ed = array('одна', 'две');
            $ret .= $Ed[$i - 1] . ' ';
         }
         else
         {
            $Ed = array('один', 'два', 'три', 'четыре', 'пять',
            'шесть', 'семь', 'восемь', 'девять' );
            $ret .= $Ed[$i - 1] . ' ';
         }
      }
      $ret .= $descr;
      return $ret;
   }

   // IN: $sum - число, например 1256.18
   function Convert($sum)
   {
      $ret = '';

      // имена данных перменных остались от предыдущей версии
      // когда скрипт конвертировал только денежные суммы
      $Kop = 0;
      $Rub = 0;

      $sum = trim($sum);
      // удалим пробелы внутри числа
      $sum = str_replace(' ', '', $sum);

      // флаг отрицательного числа
      $sign = false;
      if($sum[0] == '-')
      {
         $sum = substr($sum, 1);
         $sign = true;
      }

      // заменим запятую на точку, если она есть
      $sum = str_replace(',', '.', $sum);

      $Rub = intval($sum);
      $Kop = $sum*100 - $Rub*100;

      if($Rub)
      {
         // значение $Rub изменяется внутри функции DescrSot
         // новое значение: $Rub %= 1000000000 для миллиарда
         if($Rub >= 1000000000)
         $ret .= $this->DescrSot($Rub, 1000000000,
         array('миллиард', 'миллиарда', 'миллиардов')) . ' ';
         if($Rub >= 1000000)
         $ret .= $this->DescrSot($Rub, 1000000,
         array('миллион', 'миллиона', 'миллионов') ) . ' ';
         if($Rub >= 1000)
         $ret .= $this->DescrSot($Rub, 1000,
         array('тысяча', 'тысячи', 'тысяч'), true) . ' ';

         $ret .= $this->DescrSot($Rub, 1, $this->Mant) . ' ';

         // если необходимо поднимем регистр первой буквы
         $ret[0] = chr( ord($ret[0]) + ord('A') - ord('a') );
         // для корректно локализованных систем можно закрыть верхнюю строку
         // и раскомментировать следующую (для легкости сопровождения)
         // $ret[0] = strtoupper($ret[0]);
      }
      if($Kop < 10)
      $ret .= '0';
      $ret .= $Kop . ' ' . $this->Expon[ $this->DescrIdx($Kop) ];

      // если число было отрицательным добавим минус
      if($sign)
      $ret = '-' . $ret;
      return $ret;
   }
}

class ManyToText extends NumToText
{
   function ManyToText()
   {
      $this->SetMant( array('рубль', 'рубля', 'рублей') );
      $this->SetExpon( array('копейка', 'копейки', 'копеек') );
   }
}

class MetrToText extends NumToText
{
   function MetrToText()
   {
      $this->SetMant( array('метр', 'метра', 'метров') );
      $this->SetExpon( array('сантиметр', 'сантиметра', 'сантиметров') );
   }
}

?>
<HTML>
<HEAD>
 <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1251">
</HEAD>
<BODY>
<?php
if( isset($HTTP_POST_VARS['num']) )
{
   $mt = new ManyToText();
   $nt = new MetrToText();
   echo $mt->Convert($HTTP_POST_VARS['num']) . '<BR />';
   echo $nt->Convert($HTTP_POST_VARS['num']) . '<BR />';
}
?>
<FORM ACTION ="<?php echo $HTTP_SERVER_VARS['PHP_SELF']; ?>" METHOD="post">
Input number:<BR />
<INPUT TYPE="text" NAME="num"><BR />
<INPUT TYPE="submit" VALUE=" GET ">
</FORM>
</BODY></HTML>

Если не использовать subclassing, то строку $mt = new ManyToText() замените на:

$mt = new NumToText();
$mt->SetMant( array('рубль', 'рубля', 'рублей') );
$mt->SetExpon( array('копейка', 'копейки', 'копеек') );
echo $mt->Convert($HTTP_POST_VARS['num']);

Комментарии

Оставьте свой комментарий
А
Алексей

Вопрос новичка. А как вызвать функцию в теле страницы сайта?

K
k0rzun

Держи 5 чувак.

D
dead_star

И еще, нет проверки на корректность переданного параметра в методе Convert, если передать текст, пустое значение или ноль возникает ошибка, хотя ноль является вполне допустимым значением и нет проверки в методах SetMant и SetExpon, хотя бы на тип и размер параметра

D
dead_star

за класс спасибо сэкономил время)

@Артур:
твой код тоже не до конца корректно работает
не обрабатывает числа 1195. , 1195.1 и 1195.150

Заменил ту же строчку

$Kop = $sum*100 - $Rub*100;
на

if (strpos($sum, '.')!==false){
list(, $Kop) = explode('.', $sum, 2);
if (strlen($Kop)>2) $Kop = substr($Kop, 0, 2);
$Kop = intval($Kop);
}

А
Артур

Заменил

$Kop = $sum*100 - $Rub*100;
на

$Kop = (string)$sum;
$d = strpos($sum, ".");
if (!$d)
{
$Kop=0;
}
else // если в числе найдена точка
{
$len = strlen($Kop);
if (($len - $d) == 2)
$Kop.="0";

else if (($len - $d) == 3)
$Kop = substr($sum, $d+1);
}
Т.е. всего лишь по другому вычислил число копеек и заработало с 1195,15!
Ура!

N
nick

респект)))

V
VoJinn

Спасибо... Сэкономил много времени.:)

S
spaceman

спасибо. пашет 🙂 и "1195.15" тоже кстати нормально показывает.

T
Tolik

Автору респект. Сэкономил много времени.

S
sly

Низкий поклон за сэкономленное время!
Поймал багу: при вводе числа 1195.15 - получим "Одна тысяча сто девяносто пять рублей 15.000000000015 копеек"