Skip to content

Instantly share code, notes, and snippets.

@alexd1971
Created June 28, 2019 19:54
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexd1971/a04482b380458acf021bb9635d617a20 to your computer and use it in GitHub Desktop.
Save alexd1971/a04482b380458acf021bb9635d617a20 to your computer and use it in GitHub Desktop.
import 'dart:math';
/// Преобразует число в строку прописью
class NumericToWords {
// TODO: когда исправят баг в дарте, использовать данное регулярное выражение
//final _triadesRegExp = RegExp(r'(?<=\d)(?=(\d{3})+(?!\d))');
final _digitWords = {
0: {'male': 'ноль'},
1: {'male': 'один', 'female': 'одна'},
2: {'male': 'два', 'female': 'две'},
3: {'male': 'три'},
4: {'male': 'четыре'},
5: {'male': 'пять'},
6: {'male': 'шесть'},
7: {'male': 'семь'},
8: {'male': 'восемь'},
9: {'male': 'девять'},
10: {'male': 'десять'},
11: {'male': 'одинадцать'},
12: {'male': 'двенадцать'},
13: {'male': 'тринадцать'},
14: {'male': 'четырнадцать'},
15: {'male': 'пятнадцать'},
16: {'male': 'шеснадцать'},
17: {'male': 'семнадцать'},
18: {'male': 'восемнадцать'},
19: {'male': 'девятнадцать'},
20: {'male': 'двадцать'},
30: {'male': 'тридцать'},
40: {'male': 'сорок'},
50: {'male': 'пятьдесят'},
60: {'male': 'шестьдесят'},
70: {'male': 'семьдесят'},
80: {'male': 'восемьдесят'},
90: {'male': 'девяносто'},
100: {'male': 'сто'},
200: {'male': 'двести'},
300: {'male': 'триста'},
400: {'male': 'четыреста'},
500: {'male': 'пятьсот'},
600: {'male': 'шестьсот'},
700: {'male': 'семьсот'},
800: {'male': 'восемьсот'},
900: {'male': 'девятьсот'},
1000: {'one': 'тысяча', 'few': 'тысячи', 'many': 'тысяч'},
1000000: {'one': 'миллион', 'few': 'миллиона', 'many': 'миллионов'},
1000000000: {'one': 'миллиард', 'few': 'миллиарда', 'many': 'миллиардов'},
1000000000000: {
'one': 'триллион',
'few': 'триллиона',
'many': 'триллионов'
},
};
/// Разбивает число на триады согласно разрядам
List<String> splitToTriades(int number) {
var strNumber = number.toString();
var numberLength = strNumber.length;
var triades = List<String>();
var i = numberLength;
do {
var triade = strNumber.substring((i - 3 >= 0) ? i - 3 : 0, i);
triades.add(triade);
i -= 3;
} while (i > 0);
return List.from(triades.reversed);
// TODO: когда исправят баг в дарте, использовать данный код с регулярным выражением
// var triades = number
// .toStringAsPrecision(18)
// .replaceAllMapped(_triadesRegExp, (match) {
// return ' ';
//});
// return triades.split('.')[0]?.split(' ')?.toList();
}
/// Извлекает дробную часть числа
int extractFraction(num number) {
return int.parse(number.toStringAsFixed(2).split('.')[1]);
}
/// Преобразует число в строку прописью
/// [number] - число для преобразования в строку прописью. Максимальное - 999 триллионов
/// [lowNumberInFemenineGender] - если число заканчивается на 1,2, то оно будет преобразовано в строку в женском роде (одна, две)
///
String toWords(int number, {bool lowNumberInFemenineGender = false}) {
if (number == 0) return _digitWords[number]['male'];
var words = [];
var triades = splitToTriades(number);
// Триады разрядов будем перебирать от меньших к большим (сотни, тысячи, миллионы, миллиарды)
triades.reversed.toList().asMap().forEach((index, triade) {
final intTriade = int.parse(triade);
var triadeWords = [];
final lowNumberKindIdx =
(index == 1 || index == 0 && lowNumberInFemenineGender)
? 'female'
: 'male';
final hundred = _hundredFromTriade(intTriade);
final decimal = _decimalFromTriade(intTriade);
final lowNumber = _lowNumberFromTriade(intTriade);
final countableIdx =
lowNumber == 1 ? 'one' : (lowNumber < 5 ? 'few' : 'many');
// Добавим в строку содержащую слова по текущей триаде сотни (сто, двести, ..., девятьсот)
if (hundred > 0) triadeWords.add(_digitWords[hundred]['male']);
// Добавим в строку содержащую слова по текущей триаде десятки (десять, двадцать, ... девяносто)
if (decimal > 0) triadeWords.add(_digitWords[decimal]['male']);
// Добавим в строку содержащую слова по текущей триаде числа (один, два, ... девятнадцать)
if (lowNumber > 0)
triadeWords.add(_digitWords[lowNumber][lowNumberKindIdx] ??
_digitWords[lowNumber]['male']);
// Добавим в строку содержащую слова по текущей триаде разряды (тысяча, миллион, ... миллиард)
if (index > 0)
triadeWords.add(_digitWords[pow(10, 3 * index)][countableIdx]);
// Поскольку перебираем триады от меньших разрядов к большим,
// то получившиеся слова будем вставлять в начало строки, содержащей слова
words = triadeWords + words;
});
return words.join(' ');
}
/// Извлекает сотни из триады(числа XXX)
/// Например триада = 999, тогда результат будет: 9
///
int _hundredFromTriade(int triade) {
return ((triade ~/ 100).truncate() * 100).toInt();
}
/// Извлекает десятки из триады(числа XXX)
/// Например триада = 999, тогда результат будет: 90
/// Например триада = 911, тогда результат будет: 0
///
int _decimalFromTriade(int triade) {
final decimal = triade - _hundredFromTriade(triade);
return decimal < 20 ? 0 : ((decimal ~/ 10).truncate() * 10).toInt();
}
/// Извлекает самый первый разряд из триады из триады(числа XXX)
/// Например триада = 999, тогда результат будет: 9
/// Например триада = 911, тогда результат будет: 11
///
int _lowNumberFromTriade(int triade) {
return triade - (_hundredFromTriade(triade) + _decimalFromTriade(triade));
}
}
@Bes79
Copy link

Bes79 commented Jul 6, 2022

Hi. Thanks!!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment