Formátování čísel v Indii

Školení, která pořádám

Kvůli existenci samostatných slov pro 100 000 a 10 000 000 používají Indové jiný způsob oddělování skupin číslic – kromě trojice čísel vpravo se skupiny oddělují po dvou. Takže zatímco zbytek světa používá skupiny po třech číslicích, které se dají snadno vytvořit funkcí number_format, šestina lidí to má jinak.

Přemýšlel jsem nad funkcí, která by toto formátování vytvořila, a nic moc elegantního mě nenapadlo. Tak alespoň takhle:

<?php
/** Zformátování čísla podle Indických pravidel
* @param float
* @param int
* @return string
*/
function number_format_india($number, $decimals = 0) {
    $number = number_format($number, $decimals, ".", "");
    $x = intval($number); // oříznutí desetin
    $y = ($decimals ? substr($number, -$decimals - 1) : "");
    return strrev(preg_replace('~\\d{1,2}+~', ',\\0', strrev(substr($x, 0, -3)))) . substr($x, -3) . $y;
}
?>

Funkce nejprve zformátuje číslo bez použití oddělovačů tisíců, což je důležité kvůli správnému zaokrouhlení. Pak získá zvlášť celou a desetinnou část. Z celé části oddělí poslední tři číslice a ve zbytku přidá před každou dvojici číslic čárku. Nemůže to ale dělat zepředu, takže číslo nejdřív obrátí a pak ho zase obrátí nazpátek. Funkce je takhle složitá, aby zvládla pracovat i se zápornými čísly.

Jakub Vrána, Řešení problému, 27.9.2010, diskuse: 10 (nové: 0)

Diskuse

PR:

Možná se mýlím, ale v případě použití Zend Frameworku je tam na I8N dostupná knihovna, která mj. řeší i rozdílné číslování v zemích včetně umístění znaků měny...

ikona Jakub Vrána OpenID:

Asi to tam někde bude, ale moc se mi to nechce hledat. Pokud má někdo se Zend Frameworkem zkušenosti, tak sem může napsat kód, který tento způsob formátování čísla zajistí.

Petr Urban:

http://framework.zend.com/manual/en/zend.locale.parsing.html

ikona The Zero:

Tohle je ukázkový případ špatného použití regulárního výrazu. Vždyť x jde získat zcela jednoduše <?php $x = (int) $number; ?>.

Stejně tak poslední řádek by se dal přepsat na
<?php
return strrev(implode(',', str_split(strrev(substr($x, 0, -3)), 2))) . ',' . substr($x, -3) . $y;
?>
Ale to už výraznou úsporu kódu nepřináší a WTF faktor je zhruba stejný.

ikona Jakub Vrána OpenID:

Díky za připomínky. Přetypování na int mě vskutku vůbec nenapadlo, zvažoval jsem jen použití funkcí floor(), round() a ceil(), z nichž ani jedna použít nejde. Opravil jsem to.

Druhý návrh má zásadní problém v tom, že nefunguje s čísly menšími než 1000.

ikona Mikuláš Dítě:

Trošku jsem zapátral a nejlepší řešení mi přijde tohle:

<?php
function number_format_india($number, $decimals = 0) {
    setlocale(LC_MONETARY, 'en_IN');
    return money_format('%!.' . $decimals . 'i', $number);
}

echo
number_format_india(-100000.0005, 5); // -1,00,000.00050
echo number_format_india(10.0005, 0); // 10
?>

ikona Jakub Vrána OpenID:

Řešení je elegantní, bohužel ale není univerzálně použitelné:

1. Funkce není definovaná na Windows.

2. Požadované locales nemusí být v systému k dispozici.

ikona The Zero:

Takhle to vypadá elegantně, ale nezapomeň, že musíš vrátit locale do původního stavu. To už jsou najednou dva řádky navíc (setlocale($original) + return $result) a najednou to už tak elegantní není. Nehledě na Jakubovy připomínky.

paranoiq:

číňané často oddělují číslice po čtyřech. u znaků to vychází z tradice a slov, ale často to dělají i při zápisu arabskými číslicemi

paranoiq:

tohle by také měla řešit knihovna intl: http://www.php.net/manual/en/class.numberformatter.php

Diskuse je zrušena z důvodu spamu.

avatar © 2005-2024 Jakub Vrána. Publikované texty můžete přetiskovat pouze se svolením autora. Ukázky kódu smíte používat s uvedením autora a URL tohoto webu bez dalších omezení Creative Commons. Můžeme si tykat. Skripty předpokládají nastavení: magic_quotes_gpc=Off, magic_quotes_runtime=Off, error_reporting=E_ALL & ~E_NOTICE a očekávají předchozí zavolání mysql_set_charset. Skripty by měly být funkční v PHP >= 4.3 a PHP >= 5.0.