En PHP, existe-t-il un moyen simple de convertir un nombre en mot? Par exemple, 27 à vingt-sept .
J'ai trouvé une partie (2007/2008) du code source en ligne et comme il est protégé par le droit d'auteur, mais je peux l'utiliser librement et le modifier à ma guise. Je le place donc ici et renouvelle la licence sous CC-Wiki:
<?php
/**
* English Number Converter - Collection of PHP functions to convert a number
* into English text.
*
* This exact code is licensed under CC-Wiki on Stackoverflow.
* http://creativecommons.org/licenses/by-sa/3.0/
*
* @link http://stackoverflow.com/q/277569/367456
* @question Is there an easy way to convert a number to a Word in PHP?
*
* This file incorporates work covered by the following copyright and
* permission notice:
*
* Copyright 2007-2008 Brenton Fletcher. http://bloople.net/num2text
* You can use this freely and modify it however you want.
*/
function convertNumber($number)
{
list($integer, $fraction) = explode(".", (string) $number);
$output = "";
if ($integer{0} == "-")
{
$output = "negative ";
$integer = ltrim($integer, "-");
}
else if ($integer{0} == "+")
{
$output = "positive ";
$integer = ltrim($integer, "+");
}
if ($integer{0} == "0")
{
$output .= "zero";
}
else
{
$integer = str_pad($integer, 36, "0", STR_PAD_LEFT);
$group = rtrim(chunk_split($integer, 3, " "), " ");
$groups = explode(" ", $group);
$groups2 = array();
foreach ($groups as $g)
{
$groups2[] = convertThreeDigit($g{0}, $g{1}, $g{2});
}
for ($z = 0; $z < count($groups2); $z++)
{
if ($groups2[$z] != "")
{
$output .= $groups2[$z] . convertGroup(11 - $z) . (
$z < 11
&& !array_search('', array_slice($groups2, $z + 1, -1))
&& $groups2[11] != ''
&& $groups[11]{0} == '0'
? " and "
: ", "
);
}
}
$output = rtrim($output, ", ");
}
if ($fraction > 0)
{
$output .= " point";
for ($i = 0; $i < strlen($fraction); $i++)
{
$output .= " " . convertDigit($fraction{$i});
}
}
return $output;
}
function convertGroup($index)
{
switch ($index)
{
case 11:
return " decillion";
case 10:
return " nonillion";
case 9:
return " octillion";
case 8:
return " septillion";
case 7:
return " sextillion";
case 6:
return " quintrillion";
case 5:
return " quadrillion";
case 4:
return " trillion";
case 3:
return " billion";
case 2:
return " million";
case 1:
return " thousand";
case 0:
return "";
}
}
function convertThreeDigit($digit1, $digit2, $digit3)
{
$buffer = "";
if ($digit1 == "0" && $digit2 == "0" && $digit3 == "0")
{
return "";
}
if ($digit1 != "0")
{
$buffer .= convertDigit($digit1) . " hundred";
if ($digit2 != "0" || $digit3 != "0")
{
$buffer .= " and ";
}
}
if ($digit2 != "0")
{
$buffer .= convertTwoDigit($digit2, $digit3);
}
else if ($digit3 != "0")
{
$buffer .= convertDigit($digit3);
}
return $buffer;
}
function convertTwoDigit($digit1, $digit2)
{
if ($digit2 == "0")
{
switch ($digit1)
{
case "1":
return "ten";
case "2":
return "twenty";
case "3":
return "thirty";
case "4":
return "forty";
case "5":
return "fifty";
case "6":
return "sixty";
case "7":
return "seventy";
case "8":
return "eighty";
case "9":
return "ninety";
}
} else if ($digit1 == "1")
{
switch ($digit2)
{
case "1":
return "eleven";
case "2":
return "twelve";
case "3":
return "thirteen";
case "4":
return "fourteen";
case "5":
return "fifteen";
case "6":
return "sixteen";
case "7":
return "seventeen";
case "8":
return "eighteen";
case "9":
return "nineteen";
}
} else
{
$temp = convertDigit($digit2);
switch ($digit1)
{
case "2":
return "twenty-$temp";
case "3":
return "thirty-$temp";
case "4":
return "forty-$temp";
case "5":
return "fifty-$temp";
case "6":
return "sixty-$temp";
case "7":
return "seventy-$temp";
case "8":
return "eighty-$temp";
case "9":
return "ninety-$temp";
}
}
}
function convertDigit($digit)
{
switch ($digit)
{
case "0":
return "zero";
case "1":
return "one";
case "2":
return "two";
case "3":
return "three";
case "4":
return "four";
case "5":
return "five";
case "6":
return "six";
case "7":
return "seven";
case "8":
return "eight";
case "9":
return "nine";
}
}
Vous pouvez également utiliser la classe NumberFormatter de intl
package dans PHP. Voici un exemple de code pour vous aider à démarrer (pour la ligne de commande):
<?php
if ($argc < 3)
{
echo "usage: php {$argv[0]} lang-tag number ...\n";
exit;
}
array_shift($argv);
$lang_tag = array_shift($argv);
$nf1 = new NumberFormatter($lang_tag, NumberFormatter::DECIMAL);
$nf2 = new NumberFormatter($lang_tag, NumberFormatter::SPELLOUT);
foreach ($argv as $num)
{
echo $nf1->format($num).' is '.$nf2->format($num)."\n";
}
Il y a le paquet Numbers_Words
dans PECL. Il fait exactement ce que vous demandez. Les langues suivantes sont supportées:
J'ai réécrit le code ci-dessus pour l'adapter au format de numéro de mot écrit aux États-Unis.
function singledigit($number){
switch($number){
case 0:$Word = "zero";break;
case 1:$Word = "one";break;
case 2:$Word = "two";break;
case 3:$Word = "three";break;
case 4:$Word = "four";break;
case 5:$Word = "five";break;
case 6:$Word = "six";break;
case 7:$Word = "seven";break;
case 8:$Word = "eight";break;
case 9:$Word = "nine";break;
}
return $Word;
}
function doubledigitnumber($number){
if($number == 0){
$Word = "";
}
else{
$Word = "-".singledigit($number);
}
return $Word;
}
function doubledigit($number){
switch($number[0]){
case 0:$Word = doubledigitnumber($number[1]);break;
case 1:
switch($number[1]){
case 0:$Word = "ten";break;
case 1:$Word = "eleven";break;
case 2:$Word = "twelve";break;
case 3:$Word = "thirteen";break;
case 4:$Word = "fourteen";break;
case 5:$Word = "fifteen";break;
case 6:$Word = "sixteen";break;
case 7:$Word = "seventeen";break;
case 8:$Word = "eighteen";break;
case 9:$Word = "ninteen";break;
}break;
case 2:$Word = "twenty".doubledigitnumber($number[1]);break;
case 3:$Word = "thirty".doubledigitnumber($number[1]);break;
case 4:$Word = "forty".doubledigitnumber($number[1]);break;
case 5:$Word = "fifty".doubledigitnumber($number[1]);break;
case 6:$Word = "sixty".doubledigitnumber($number[1]);break;
case 7:$Word = "seventy".doubledigitnumber($number[1]);break;
case 8:$Word = "eighty".doubledigitnumber($number[1]);break;
case 9:$Word = "ninety".doubledigitnumber($number[1]);break;
}
return $Word;
}
function unitdigit($numberlen,$number){
switch($numberlen){
case 3:case 6:case 9:case 12:$Word = "hundred";break;
case 4:case 5:$Word = "thousand";break;
case 7:case 8:$Word = "million";break;
case 10:case 11:$Word = "billion";break;
}
return $Word;
}
function numberToWord($number){
$numberlength = strlen($number);
if ($numberlength == 1) {
return singledigit($number);
}elseif ($numberlength == 2) {
return doubledigit($number);
}
else {
$Word = "";
$wordin = "";
switch ($numberlength ) {
case 5:case 8: case 11:
if($number[0] >0){
$unitdigit = unitdigit($numberlength,$number[0]);
$Word = doubledigit($number[0].$number[1]) ." ".$unitdigit." ";
return $Word." ".numberToWord(substr($number,2));
}
else{
return $Word." ".numberToWord(substr($number,1));
}
break;
default:
if($number[0] >0){
$unitdigit = unitdigit($numberlength,$number[0]);
$Word = singledigit($number[0]) ." ".$unitdigit." ";
}
return $Word." ".numberToWord(substr($number,1));
}
}
}
J'avais besoin d'une solution qui insère 'et' dans la chaîne renvoyée et la transforme en une phrase - généralement comme le dirait un humain. J'ai donc légèrement adapté une solution différente, publiée car je pensais que cela pourrait être utile à quelqu'un.
4,835,301 returns "Four million eight hundred and thirty five thousand three hundred and one."
Code
function convertNumber($num = false)
{
$num = str_replace(array(',', ''), '' , trim($num));
if(! $num) {
return false;
}
$num = (int) $num;
$words = array();
$list1 = array('', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten', 'eleven',
'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
);
$list2 = array('', 'ten', 'twenty', 'thirty', 'forty', 'fifty', 'sixty', 'seventy', 'eighty', 'ninety', 'hundred');
$list3 = array('', 'thousand', 'million', 'billion', 'trillion', 'quadrillion', 'quintillion', 'sextillion', 'septillion',
'octillion', 'nonillion', 'decillion', 'undecillion', 'duodecillion', 'tredecillion', 'quattuordecillion',
'quindecillion', 'sexdecillion', 'septendecillion', 'octodecillion', 'novemdecillion', 'vigintillion'
);
$num_length = strlen($num);
$levels = (int) (($num_length + 2) / 3);
$max_length = $levels * 3;
$num = substr('00' . $num, -$max_length);
$num_levels = str_split($num, 3);
for ($i = 0; $i < count($num_levels); $i++) {
$levels--;
$hundreds = (int) ($num_levels[$i] / 100);
$hundreds = ($hundreds ? ' ' . $list1[$hundreds] . ' hundred' . ( $hundreds == 1 ? '' : '' ) . ' ' : '');
$tens = (int) ($num_levels[$i] % 100);
$singles = '';
if ( $tens < 20 ) {
$tens = ($tens ? ' and ' . $list1[$tens] . ' ' : '' );
} elseif ($tens >= 20) {
$tens = (int)($tens / 10);
$tens = ' and ' . $list2[$tens] . ' ';
$singles = (int) ($num_levels[$i] % 10);
$singles = ' ' . $list1[$singles] . ' ';
}
$words[] = $hundreds . $tens . $singles . ( ( $levels && ( int ) ( $num_levels[$i] ) ) ? ' ' . $list3[$levels] . ' ' : '' );
} //end for loop
$commas = count($words);
if ($commas > 1) {
$commas = $commas - 1;
}
$words = implode(' ', $words);
$words = preg_replace('/^\s\b(and)/', '', $words );
$words = trim($words);
$words = ucfirst($words);
$words = $words . ".";
return $words;
}
En utilisant la classe NumberFormatter, il est simple d’obtenir une conversion en mots.
<?php
$number = '12345';
$locale = 'en_US';
$fmt = numfmt_create($locale, NumberFormatter::SPELLOUT);
$in_words = numfmt_format($fmt, $number);
print_r($in_words);
// twelve thousand three hundred forty-five
?>
Vous pouvez utiliser la classe NumberFormatter Class :
$f = new NumberFormatter("en", NumberFormatter::SPELLOUT);
echo $f->format($myNumber);
Voici un petit cours que j'ai écrit ce soir. Mises en garde:
longform
ne gère pas les décimales. Cela les efface. N'hésitez pas à modifier cela et à ajouter cette fonctionnalité si vous le souhaitez. numberformat
fait des décimales, mais n’arrondit pas. J'ai dû créer une nouvelle fonction numberformat
en raison des limitations inhérentes à PHP avec les tailles entières. Je traduisais tellement de chiffres que lorsque j'ai utilisé number_format()
pour vérifier mes traductions, il m'a fallu 30 minutes pour comprendre que mes traductions n'étaient pas fausses, mais number_format
l'était. 2,147,483,647
(2 milliards et plus). Les versions 64 bits gèrent jusqu'à 9 quintillion
ou quelque chose du genre. MAIS cela n’a aucune importance ici tant que vous insérez les nombres dans la méthode longform
sous la forme string
. J'ai composé un nombre correct à 306 chiffres sur ajax
à partir d'un formulaire Web, à condition que je le transmette au serveur sous la forme ''+number
. Ainsi, cette classe traduira des nombres allant jusqu’à 999 Centillion, 999 etc.
(une chaîne de 9 306 caractères, par exemple). N'importe quel nombre supérieur à cela et la fonction ne fait que renvoyer un message muet.
Usage:
$number = '999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999';
reallyBig::longform($number);
Le deuxième paramètre optionnel boolean est défini par défaut sur true, ce qui ajoute des virgules du mieux qu’il peut au bon endroit, afin de rendre le nombre plus lisible.
En passant, vous pouvez mettre un -
au début si vous voulez que ce soit négatif, mais tous les autres caractères inclus dans la chaîne entrée seront supprimés. Par exemple:
reallyBig::longform('-C55LL-M5-4-a-9u7-71m3-M8');
affichera: negative five billion, five hundred fifty-four million, nine hundred seventy-seven thousand, one hundred thirty-eight
La méthode numberformat
n'est nécessaire pour aucune autre méthode. C'est juste là si vous voulez vérifier un nombre traduit très long. Étant donné que toutes ces fonctions traitent les nombres comme des chaînes, elles ne se heurtent pas aux limites de PHP.
La seule raison pour laquelle j'ai arrêté à 999 centillions, c'est parce que centillion était le dernier chiffre du site Web que je cherchais alors que je ne me souvenais plus de ce qui arrivait après un décillion.
class reallyBig
{
private static $map, $strings;
private static function map()
{
$map = array();
$num = 1;
$count = 1;
while($num < 307)
{
if($count == 1) $map[$num] = $num+2;
elseif($count == 2) $map[$num] = $num+1;
else
{
$map[$num] = $num;
$count = 0;
}
$count++;
$num++;
}
return $map;
}
private static function strings()
{
return array
(
6 => 'thousand',
9 => 'million',
12 => 'billion',
15 => 'trillion',
18 => 'quadrillion',
21 => 'quintillion',
24 => 'sextillion',
27 => 'septillion',
30 => 'octillion',
33 => 'nonillion',
36 => 'decillion',
39 => 'undecillion',
42 => 'duodecillion',
45 => 'tredecillion',
48 => 'quattuordecillion',
51 => 'quindecillion',
54 => 'sexdecillion',
57 => 'septendecillion',
60 => 'octodecillion',
63 => 'novemdecillion',
66 => 'vigintillion',
69 => 'unvigintillion',
72 => 'duovigintillion',
75 => 'trevigintillion',
78 => 'quattuorvigintillion',
81 => 'quinvigintillion',
84 => 'sexvigintillion',
87 => 'septenvigintillion',
90 => 'octovigintillion',
93 => 'novemvigintillion',
96 => 'trigintillion',
99 => 'untrigintillion',
102 => 'duotrigintillion',
105 => 'tretrigintillion',
108 => 'quattuortrigintillion',
111 => 'quintrigintillion',
114 => 'sextrigintillion',
117 => 'septentrigintillion',
120 => 'octotrigintillion',
123 => 'novemtrigintillion',
126 => 'quadragintillion',
129 => 'unquadragintillion',
132 => 'duoquadragintillion',
135 => 'trequadragintillion',
138 => 'quattuorquadragintillion',
141 => 'quinquadragintillion',
144 => 'sexquadragintillion',
147 => 'septenquadragintillion',
150 => 'octoquadragintillion',
153 => 'novemquadragintillion',
156 => 'quinquagintillion',
159 => 'unquinquagintillion',
162 => 'duoquinquagintillion',
165 => 'trequinquagintillion',
168 => 'quattuorquinquagintillion',
171 => 'quinquinquagintillion',
174 => 'sexquinquagintillion',
177 => 'septenquinquagintillion',
180 => 'octoquinquagintillion',
183 => 'novemquinquagintillion',
186 => 'sexagintillion',
189 => 'unsexagintillion',
192 => 'duosexagintillion',
195 => 'tresexagintillion',
198 => 'quattuorsexagintillion',
201 => 'quinsexagintillion',
204 => 'sexsexagintillion',
207 => 'septensexagintillion',
210 => 'octosexagintillion',
213 => 'novemsexagintillion',
216 => 'septuagintillion',
219 => 'unseptuagintillion',
222 => 'duoseptuagintillion',
225 => 'treseptuagintillion',
228 => 'quattuorseptuagintillion',
231 => 'quinseptuagintillion',
234 => 'sexseptuagintillion',
237 => 'septenseptuagintillion',
240 => 'octoseptuagintillion',
243 => 'novemseptuagintillion',
246 => 'octogintillion',
249 => 'unoctogintillion',
252 => 'duooctogintillion',
255 => 'treoctogintillion',
258 => 'quattuoroctogintillion',
261 => 'quinoctogintillion',
264 => 'sexoctogintillion',
267 => 'septenoctogintillion',
270 => 'octooctogintillion',
273 => 'novemoctogintillion',
276 => 'nonagintillion',
279 => 'unnonagintillion',
282 => 'duononagintillion',
285 => 'trenonagintillion',
288 => 'quattuornonagintillion',
291 => 'quinnonagintillion',
294 => 'sexnonagintillion',
297 => 'septennonagintillion',
300 => 'octononagintillion',
303 => 'novemnonagintillion',
306 => 'centillion',
);
}
public static function longform($number = string, $commas = true)
{
$negative = substr($number, 0, 1) == '-' ? 'negative ' : '';
list($number) = explode('.', $number);
$number = trim(preg_replace("/[^0-9]/u", "", $number));
$number = (string)(ltrim($number,'0'));
if(empty($number)) return 'zero';
$length = strlen($number);
if($length < 2) return $negative.self::ones($number);
if($length < 3) return $negative.self::tens($number);
if($length < 4) return $commas ? $negative.str_replace('hundred ', 'hundred and ', self::hundreds($number)) : $negative.self::hundreds($number);
if($length < 307)
{
self::$map = self::map();
self::$strings = self::strings();
$result = self::beyond($number, self::$map[$length]);
if(!$commas) return $negative.$result;
$strings = self::$strings;
$thousand = array_shift($strings);
foreach($strings as $string) $result = str_replace($string.' ', $string.', ', $result);
if(strpos($result, 'thousand') !== false) list($junk,$remainder) = explode('thousand', $result);
else $remainder = $result;
return strpos($remainder, 'hundred') !== false ? $negative.str_replace('thousand ', 'thousand, ', $result) : $negative.str_replace('thousand ', 'thousand and ', $result);
}
return 'a '.$negative.'number too big for your britches';
}
private static function ones($number)
{
$ones = array('zero','one','two','three','four','five','six','seven','eight','nine');
return $ones[$number];
}
private static function tens($number)
{
$number = (string)(ltrim($number,'0'));
if(strlen($number) < 2) return self::ones($number);
if($number < 20)
{
$teens = array('ten','eleven','twelve','thirteen','fourteen','fifteen','sixteen','seventeen','eighteen','nineteen');
return $teens[($number-10)];
}
else
{
$tens = array('','','twenty','thirty','forty','fifty','sixty','seventy','eighty','ninety');
$Word = $tens[$number[0]];
return empty($number[1]) ? $Word : $Word.'-'.self::ones($number[1]);
}
}
private static function hundreds($number)
{
$number = (string)(ltrim($number,'0'));
if(strlen($number) < 3) return self::tens($number);
$Word = self::ones($number[0]).' hundred';
$remainder = substr($number, -2);
if(ltrim($remainder,'0') != '') $Word .= ' '.self::tens($remainder);
return $Word;
}
private static function beyond($number, $limit)
{
$number = (string)(ltrim($number,'0'));
$length = strlen($number);
if($length < 4) return self::hundreds($number);
if($length < ($limit-2)) return self::beyond($number, self::$map[($limit-3)]);
if($length == $limit) $Word = self::hundreds(substr($number, 0, 3), true);
elseif($length == ($limit-1)) $Word = self::tens(substr($number, 0, 2));
else $Word = self::ones($number[0]);
$Word .= ' '.self::$strings[$limit];
$sub = ($limit-3);
$remainder = substr($number, -$sub);
if(ltrim($remainder,'0') != '') $Word .= ' '.self::beyond($remainder, self::$map[$sub]);
return $Word;
}
public static function numberformat($number, $fixed = 0, $dec = '.', $thou = ',')
{
$negative = substr($number, 0, 1) == '-' ? '-' : '';
$number = trim(preg_replace("/[^0-9\.]/u", "", $number));
$number = (string)(ltrim($number,'0'));
$fixed = (int)$fixed;
if(!is_numeric($fixed)) $fixed = 0;
if(strpos($number, $dec) !== false) list($number,$decimals) = explode($dec, $number);
else $decimals = '0';
if($fixed) $decimals = '.'.str_pad(substr($decimals, 0, $fixed), $fixed, 0, STR_PAD_RIGHT);
else $decimals = '';
$thousands = array_map('strrev', array_reverse(str_split(strrev($number), 3)));
return $negative.implode($thou,$thousands).$decimals;
}
}