Как я могу отформатировать числа в виде строки валюты в JavaScript? - PullRequest
1578 голосов
/ 29 сентября 2008

Я бы хотел отформатировать цену в JavaScript.
Я хотел бы функцию, которая принимает float в качестве аргумента и возвращает string в следующем формате:

"$ 2,500.00"

Какой лучший способ сделать это?

Ответы [ 62 ]

2 голосов
/ 21 февраля 2015

у многих ответов были полезные идеи, но ни один из них не соответствовал моим потребностям. Поэтому я использовал все идеи и построил этот пример:

function Format_Numb( fmt){
    var decimals = isNaN(decimals) ? 2 : Math.abs(decimals);
    if(typeof decSgn==="undefined") decSgn = ".";
    if(typeof kommaSgn==="undefined") kommaSgn= ",";

    var s3digits=/(\d{1,3}(?=(\d{3})+(?=[.]|$))|(?:[.]\d*))/g;
    var dflt_nk="00000000".substring(0,decimals);

    //--------------------------------
    // handler for pattern: "%m"
    var _f_money= function( v_in){
                var v=v_in.toFixed(decimals);
                var add_nk=",00";
                var arr=    v.split(".");
                return     arr[0].toString().replace(s3digits, function ($0) {
                                    return ($0.charAt(0)==".")
                                        ? ((add_nk=""),(kommaSgn + $0.substring(1)))
                                        : ($0 + decSgn);
                        })
                        + (    (decimals > 0)
                                ?    (    kommaSgn
                                        + (
                                            (arr.length > 1)
                                            ? arr[1]
                                            : dflt_nk
                                        )
                                    )
                                :    ""                    
                        );
    }

    // handler for pattern: "%<len>[.<prec>]f"
    var _f_flt= function( v_in,l,prec){
        var v=(typeof prec !== "undefined") ? v_in.toFixed(prec):v_in;
        return ((typeof l !== "undefined")&&( (l=l-v.length) > 0))
                ?(Array(l+1).join(" ") + v)
                :v;
    }

    // handler for pattern: "%<len>x"
    var _f_hex= function( v_in,l,flUpper){
        var v=    Math.round(v_in).toString(16);
        if(flUpper)    v=v.toUpperCase();
        return ((typeof l !== "undefined")&&( (l=l-v.length) > 0))
                ?(Array(l+1).join("0") + v)
                :v;        
    }

    //...can be extended..., just add the function, f.e.:    var _f_octal= function( v_in,...){
    //--------------------------------

    if( typeof(fmt)!=="undefined"){
        //...can be extended..., just add the char,f.e."O":    MFX -> MFXO
        var rpatt=/(?:%([^%"MFX]*)([MFX]))|(?:"([^"]*)")|("|%%)/gi;
        var _qu=    "\"";
        var _mask_qu=    "\\\"";
        var str=    fmt.toString().replace( rpatt,function($0,$1,$2,$3,$4){
                                var f;
                                if(typeof $1 !== "undefined"){
                                    switch($2.toUpperCase()){
                                        case "M":    f= "_f_money(v)";    break;
                                        case "F":    var    n_dig0,n_dig1;
                                                var    re_flt=/^(?:(\d))*(?:[.](\d))*$/;
                                                $1.replace(re_flt,function($0,$1,$2){
                                                    n_dig0=$1;
                                                    n_dig1=$2;
                                                });
                                                f= "_f_flt(v," + n_dig0 + "," + n_dig1 + ")";    break;
                                        case "X":    var    n_dig="undefined";
                                                var    re_flt=/^(\d*)$/;
                                                $1.replace(re_flt,function($0){
                                                    if($0!="")n_dig=$0;
                                                });
                                                f= "_f_hex(v," + n_dig + "," + ($2=="X") + ")";    break;
                                        //...can be extended..., f.e.:    case "O":
                                    }
                                    return "\"+"+f+"+\"";
                                } else if(typeof $3 !== "undefined"){
                                    return _mask_qu + $3 + _mask_qu;
                                } else {
                                    return ($4==_qu)?_mask_qu:$4.charAt(0);
                                }
                            });
        var cmd=        "return function(v){"
                +        "if(typeof v === \"undefined\")return \"\";"    //null returned as empty string
                +        "if(!v.toFixed)return v.toString();"        //not numb returned as string
                +        "return \"" + str + "\";"
                +    "}";

        //...can be extended..., just add the function name in the 2 places:
        return new Function( "_f_money,_f_flt,_f_hex", cmd)(_f_money,_f_flt,_f_hex);
    }
}

Во-первых, мне нужно было формат-строковое определение в стиле C, которое должно быть гибким, но очень простым в использовании , и я определил его следующим образом; шаблоны:

%[<len>][.<prec>]f        float, example "%f", "%8.2d", "%.3f"
%m                        money
%[<len>]x                 hexadecimal lower case, example "%x", "%8x"
%[<len>]X                 hexadecimal upper case, example "%X", "%8X"

, поскольку мне не нужно форматировать другие, тогда как в Евро, я реализовал только "% m". Но это легко продлить ... Как и в C, строка формата представляет собой строку, содержащую шаблоны, F.E. для евро : "% m €" (возвращает строки типа "8.129,33 €")

Помимо гибкости мне понадобилось очень быстрое решение для обработки таблиц . Это означает, что при обработке тысяч ячеек обработка строки формата не должна выполняться более одного раза . Вызов типа "format (value, fmt)" для меня неприемлем, но его нужно разделить на два этапа:

// var formatter = Format_Numb( "%m €");  
//simple example for Euro...

//   but we use a complex example: 

var formatter = Format_Numb("a%%%3mxx \"zz\"%8.2f°\"  >0x%8X<");

// formatter is now a function, which can be used more than once (this is an example, that can be tested:) 

var v1= formatter( 1897654.8198344); 

var v2= formatter( 4.2); 

... (and thousands of rows)

Также для производительности _f_money заключает регулярное выражение;

В-третьих, вызов типа "format (value, fmt)" неприемлем, потому что: Хотя должна быть возможность форматировать разные коллекции объектов (например, ячеек столбца) с разными масками, я не хочу иметь что-то для обработки строк форматирования на этапе обработки. На данный момент я хочу только использовать форматирование, как в

для (переменная ячейка в ячейках) {do_something (cell.col.formatter ( cell.value)); }

Какой формат - может быть, он определен в ini, в xml для каждого столбца или где-то еще ..., но анализ и настройка форматов или работа с интернационализацией обрабатывается в совершенно другом месте , и там я хочу назначить средство форматирования для коллекции, не задумываясь о проблемах производительности:

col.formatter = Format_Numb (_getFormatForColumn (...));

В-четвертых, я хотел «толерантное» решение , так что проходя мимо. строка вместо числа должна возвращать просто строку, но «null» должна возвращать пустую строку.

(Также форматирование "% 4.2f" не должно обрезать что-либо, если значение слишком велико.)

И последнее, но не менее важное - оно должно быть читаемым и легко расширяемым , БЕЗ каких-либо последствий для производительности ... Например, если кому-то нужны «восьмеричные значения», пожалуйста, обратитесь к строкам со словами «... можно расширить ...» - я думаю, это должно быть очень простой задачей.

Я сосредоточился на производительности. Каждая «процедура обработки» (например, _f_money) может быть инкапсулирована, оптимизирована или заменена другими идеями в этом или других потоках без изменения «процедур подготовки» (анализа строк формата и создания функций), которые должны обрабатываться только один раз и в этом смысл не настолько критичен по производительности, как конверсионные вызовы тысяч номеров.

Для всех, кто предпочитает методы чисел:

Number.prototype.format_euro=( function(formatter){
    return function(){ return formatter(this); }})
    (Format_Numb( "%m €"));

var v_euro= (8192.3282).format_euro(); //results: 8.192,33 €

Number.prototype.format_hex= (function(formatter){
    return function(){ return formatter(this); }})
    (Format_Numb( "%4x"));

var v_hex= (4.3282).format_hex();

Хотя я что-то тестировал, в коде может быть много ошибок. Так что это не готовый модуль, а просто идея и отправная точка для не-js-экспертов, таких как я. Код содержит много и мало модифицированных идей из множества сообщений stackoverflow; извините, я не могу сослаться на них всех, но спасибо всем экспертам.

2 голосов
/ 21 марта 2013

Потому что почему бы не добавить еще один ответ. Я основал это на ответ от VisioN.

function format (val) {
  val = (+val).toLocaleString();
  val = (+val).toFixed(2);
  val += "";
  return val.replace(/(\d)(?=(\d{3})+(?:\.\d+)?$)/g, "$1" + format.thousands);
}
(function (isUS) {
  format.decimal =   isUS ? "." : ",";
  format.thousands = isUS ? "," : ".";
}(("" + (+(0.00).toLocaleString()).toFixed(2)).indexOf(".") > 0));

Я тестировал с входами:

[   ""
  , "1"
  , "12"
  , "123"
  , "1234"
  , "12345"
  , "123456"
  , "1234567"
  , "12345678"
  , "123456789"
  , "1234567890"
  , ".12"
  , "1.12"
  , "12.12"
  , "123.12"
  , "1234.12"
  , "12345.12"
  , "123456.12"
  , "1234567.12"
  , "12345678.12"
  , "123456789.12"
  , "1234567890.12"
  , "1234567890.123"
  , "1234567890.125"
].forEach(function (item) {
  console.log(format(item));
});

И получил эти результаты:

0.00
1.00
12.00
123.00
1,234.00
12,345.00
123,456.00
1,234,567.00
12,345,678.00
123,456,789.00
1,234,567,890.00
0.12
1.12
12.12
123.12
1,234.12
12,345.12
123,456.12
1,234,567.12
12,345,678.12
123,456,789.12
1,234,567,890.12
1,234,567,890.12
1,234,567,890.13

Просто для удовольствия.

2 голосов
/ 29 января 2015

Мне нравится самый короткий ответ от VisionN, за исключением случаев, когда мне нужно изменить его для числа без десятичной точки (123 доллара вместо 123,00 долларов США), он не работает, поэтому вместо быстрого копирования / вставки мне нужно расшифровать тайный синтаксис JavaScript регулярное выражение.

Вот оригинальное решение

n.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');

Я сделаю это немного дольше:

var re = /\d(?=(\d{3})+\.)/g;
var subst = '$&,';
n.toFixed(2).replace(re, subst);

Re часть здесь (поиск части в строке заменяет) означает

  1. Найти все цифры (\d)
  2. Вслед за (?= ...) (взгляд вперед)
  3. Одна или несколько групп ( ... )+
  4. Точно 3 цифры (\d{3})
  5. Окончание с точкой (\.)
  6. Сделайте это для всех случаев (g)

Subst здесь часть означает

  1. Каждый раз, когда есть совпадение, заменяйте его на себя ($&), за которым следует запятая.

Поскольку мы используем string.replace, весь остальной текст в строке остается прежним, и только найденные цифры (те, за которыми следуют 3,6,9 и т. Д., Другие цифры) получают дополнительную запятую.

Таким образом, в числе 1234567,89 цифры 1 и 4 соответствуют условию ( 1 23 4 567,89) и заменяются на " 1,"и" 4,", в результате чего 1 234 567,89.

Если нам вообще не нужна десятичная точка в долларах (то есть 123 доллара вместо 123,00 долларов), мы можем изменить регулярное выражение следующим образом:

var re2 = /\d(?=(\d{3})+$)/g;

Он опирается на конец строки ($) вместо точки (\.), и окончательное выражение будет (обратите внимание также toFixed(0)):

n.toFixed(0).replace(/\d(?=(\d{3})+$)/g, '$&,');

Это выражение даст

1234567.89 -> 1,234,567

Также вместо конца строки ($) в приведенном выше регулярном выражении вы также можете выбрать границу слова (\b).

Заранее извиняюсь за неправильную интерпретацию какой-либо части обработки регулярных выражений.

2 голосов
/ 05 июля 2012

Coffeescript для популярного ответа Патрика выше:

Number::formatMoney = (decimalPlaces, decimalChar, thousandsChar) ->  
  n = this  
  c = decimalPlaces  
  d = decimalChar  
  t = thousandsChar  
  c = (if isNaN(c = Math.abs(c)) then 2 else c)  
  d = (if d is undefined then "." else d)  
  t = (if t is undefined then "," else t)  
  s = (if n < 0 then "-" else "")  
  i = parseInt(n = Math.abs(+n or 0).toFixed(c)) + ""  
  j = (if (j = i.length) > 3 then j % 3 else 0)  
  s + (if j then i.substr(0, j) + t else "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (if c then d + Math.abs(n - i).toFixed(c).slice(2) else "")  
1 голос
/ 27 мая 2010
function getMoney(A){
    var a = new Number(A);
    var b = a.toFixed(2); //get 12345678.90
    a = parseInt(a); // get 12345678
    b = (b-a).toPrecision(2); //get 0.90
    b = parseFloat(b).toFixed(2); //in case we get 0.0, we pad it out to 0.00
    a = a.toLocaleString();//put in commas - IE also puts in .00, so we'll get 12,345,678.00
    //if IE (our number ends in .00)
    if(a < 1 && a.lastIndexOf('.00') == (a.length - 3))
    {
        a=a.substr(0, a.length-3); //delete the .00
    }
    return a+b.substr(1);//remove the 0 from b, then return a + b = 12,345,678.90
}
alert(getMoney(12345678.9));

Это работает в FF и IE

1 голос
/ 06 апреля 2015

Уже есть хорошие ответы. Вот простая попытка для развлечения:

function currencyFormat(no) {
  var ar = (+no).toFixed(2).split('.');
  return [
      numberFormat(ar[0]|0),
      '.', 
      ar[1]
  ].join('');
}


function numberFormat(no) {
  var str = no + '';
  var ar = [];
  var i  = str.length -1;

  while( i >= 0 ) {
    ar.push( (str[i-2]||'') + (str[i-1]|| '')+ (str[i]|| ''));
    i= i-3;
  }
  return ar.reverse().join(',');  
}

Выполнить несколько примеров

console.log(
  currencyFormat(1),
  currencyFormat(1200),
  currencyFormat(123),
  currencyFormat(9870000),
  currencyFormat(12345),
  currencyFormat(123456.232)
)
1 голос
/ 23 ноября 2016

Вот простой форматировщик в vanilla JS:

function numberFormatter (num) {
        console.log(num)
    var wholeAndDecimal = String(num.toFixed(2)).split(".");
    console.log(wholeAndDecimal)
    var reversedWholeNumber = Array.from(wholeAndDecimal[0]).reverse();
    var formattedOutput = [];

    reversedWholeNumber.forEach( (digit, index) => {
        formattedOutput.push(digit);
        if ((index + 1) % 3 === 0 && index < reversedWholeNumber.length - 1) {
            formattedOutput.push(",");
        }
    })

    formattedOutput = formattedOutput.reverse().join('') + "." + wholeAndDecimal[1];

    return formattedOutput;

}
1 голос
/ 09 января 2016

Я хочу внести свой вклад в это:

function toMoney(amount) {
    neg = amount.charAt(0);
    amount= amount.replace(/\D/g, '');
    amount= amount.replace(/\./g  , '');
    amount= amount.replace(/\-/g, '');

    var numAmount = new Number(amount); 
    amount= numAmount .toFixed(0).replace(/./g, function(c, i, a) {
        return i > 0 && c !== "," && (a.length - i) % 3 === 0 ? "." + c : c;
    });

    if(neg == '-')
        return neg+amount;
    else
        return amount;
}

Это позволяет вам преобразовывать числа в текстовом поле, в котором вы можете использовать только цифры (рассмотрите этот сценарий)

Это очистит текстовое поле, в котором могут быть только цифры, даже если вы вставите строку с цифрами и буквами или любым символом

<html>
<head>
<script language=="Javascript">
function isNumber(evt) {    
    var theEvent = evt || window.event;
    var key = theEvent.keyCode || theEvent.which;
    key = String.fromCharCode(key);
    if (key.length == 0) return;
    var regex = /^[0-9\-\b]+$/;
    if (!regex.test(key)) {
        theEvent.returnValue = false;
        if (theEvent.preventDefault) theEvent.preventDefault();
    }
}
function toMoney(amount) {
    neg = amount.charAt(0);
    amount= amount.replace(/\D/g, '');
    amount= amount.replace(/\./g  , '');
    amount= amount.replace(/\-/g, '');

    var numAmount = new Number(amount); 
    amount= numAmount .toFixed(0).replace(/./g, function(c, i, a) {
        return i > 0 && c !== "," && (a.length - i) % 3 === 0 ? "." + c : c;
    });

    if(neg == '-')
        return neg+amount;
    else
        return amount;
}
function clearText(inTxt, newTxt, outTxt) {
    inTxt = inTxt.trim();
    newTxt = newTxt.trim();
    if(inTxt == '' || inTxt == newTxt) 
        return outTxt;

    return inTxt;   
}

function fillText(inTxt, outTxt) {
    inTxt = inTxt.trim();
    if(inTxt != '') 
        outTxt = inTxt;

    return outTxt;
}
</script>
</head>
<body>
$ <input name=reca2 id=reca2 type=text value="0" onFocus="this.value = clearText(this.value, '0', '');" onblur="this.value = fillText(this.value, '0'); this.value = toMoney(this.value);" onKeyPress="isNumber(event);" style="width:80px;" />
</body>
</html>
1 голос
/ 10 сентября 2014

Мне нравится это просто:

function formatPriceUSD(price) {
    var strPrice = price.toFixed(2).toString();
    var a = strPrice.split('');

    if (price > 1000000000)
        a.splice(a.length - 12, 0, ',');

    if (price > 1000000)
        a.splice(a.length - 9, 0, ',');

    if (price > 1000)
        a.splice(a.length - 6, 0, ',');

    return '$' + a.join("");
}
0 голосов
/ 22 июля 2011

Вот быстрый способ с помощью регулярных выражений и замены.

function formatCurrency( number, dp, ts ) {
  var num = parseFloat( number ); //convert to float
  var pw; //for IE
  dp = parseInt( dp, 10 ); //decimal point
  dp = isNaN( dp ) ? 2 : dp; //default 2 decimal point
  ts = ts || ','; //thousands separator

  return num != number ? 
    false : //return false for NaN
    ( ( 0.9 ).toFixed( 0 ) == '1' ? //for cater IE toFixed bug
        num.toFixed( dp ) : //format to fix n decimal point with round up
        ( Math.round( num * ( pw = Math.pow( 10, dp ) || 1 ) ) / pw ).toFixed( dp ) //for fix ie toFixed bug on round up value like 0.9 in toFixed
    ).replace( /^(-?\d{1,3})((\d{3})*)(\.\d+)?$/, function( all, first, subsequence, dmp, dec ) { //separate string into different parts
      return ( first || '' ) + subsequence.replace( /(\d{3})/g, ts + '$1' ) + ( dec || '' ); //add thousands seperator and re-join all parts
    } );
}
...