у многих ответов были полезные идеи, но ни один из них не соответствовал моим потребностям.
Поэтому я использовал все идеи и построил этот пример:
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; извините, я не могу сослаться на них всех, но спасибо всем экспертам.