Created
June 28, 2019 19:54
-
-
Save alexd1971/a04482b380458acf021bb9635d617a20 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Hi. Thanks!!!