JavaScript equivalente a printf/String.Format
(30)
Estoy buscando un buen equivalente en JavaScript de C / PHP printf()
o para programadores de C # / Java, String.Format()
( IFormatProvider
for .NET).
Mi requisito básico es un formato de miles de separadores para números por ahora, pero algo que maneje muchas combinaciones (incluyendo fechas) sería bueno.
Me doy cuenta de que la biblioteca Ajax de Microsoft proporciona una versión de String.Format()
, pero no queremos toda la sobrecarga de ese marco.
Formato de número en JavaScript
Llegué a esta página de preguntas con la esperanza de encontrar la forma de dar formato a los números en JavaScript, sin presentar otra biblioteca. Esto es lo que he encontrado:
Redondear números de punto flotante
El equivalente de sprintf("%.2f", num)
en JavaScript parece ser num.toFixed(2)
, que se formatea num
a 2 lugares decimales, con redondeo (pero vea el comentario de @ ars265 sobre Math.round
continuación).
(12.345).toFixed(2); // returns "12.35" (rounding!)
(12.3).toFixed(2); // returns "12.30" (zero padding)
Forma exponencial
El equivalente de sprintf("%.2e", num)
es num.toExponential(2)
.
(33333).toExponential(2); // "3.33e+4"
Hexadecimales y otras bases.
Para imprimir números en la base B, intente num.toString(B)
. JavaScript admite la conversión automática hacia y desde las bases 2 a 36 (además, algunos navegadores tienen una compatibilidad limitada con la codificación base64 ).
(3735928559).toString(16); // to base 16: "deadbeef"
parseInt("deadbeef", 16); // from base 16: 3735928559
Páginas de referencia
Tutorial rápido sobre el formato de número JS
Página de referencia de Mozilla para toFixed () (con enlaces a toPrecision (), toExponential (), toLocaleString (), ...)
+1 Zippo con la excepción de que el cuerpo de la función debe ser como se muestra a continuación o, de lo contrario, agrega la cadena actual en cada iteración:
String.prototype.format = function() {
var formatted = this;
for (var arg in arguments) {
formatted = formatted.replace("{" + arg + "}", arguments[arg]);
}
return formatted;
};
A partir de ES6 se pueden usar cadenas de plantillas :
let soMany = 10;
console.log(`This is ${soMany} times easier!`);
// "This is 10 times easier!
Tenga en cuenta que las cadenas de plantillas están rodeadas por comillas invertidas `en lugar de comillas (simples).
Para mayor información:
https://developers.google.com/web/updates/2015/01/ES6-Template-Strings
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/template_strings
Nota: compruebe el sitio de Mozilla para encontrar una lista de navegadores compatibles.
Agregando a la respuesta de zippoxer
, uso esta función:
String.prototype.format = function () {
var a = this, b;
for (b in arguments) {
a = a.replace(/%[a-z]/, arguments[b]);
}
return a; // Make chainable
};
var s = ''Hello %s The magic number is %d.'';
s.format(''world!'', 12); // Hello World! The magic number is 12.
También tengo una versión no prototipo que uso más a menudo por su sintaxis similar a Java:
function format() {
var a, b, c;
a = arguments[0];
b = [];
for(c = 1; c < arguments.length; c++){
b.push(arguments[c]);
}
for (c in b) {
a = a.replace(/%[a-z]/, b[c]);
}
return a;
}
format(''%d ducks, 55 %s'', 12, ''cats''); // 12 ducks, 55 cats
Actualización ES 2015
Todas las nuevas cosas geniales en ES 2015 hacen esto mucho más fácil:
function format(fmt, ...args){
return fmt
.split("%%")
.reduce((aggregate, chunk, i) =>
aggregate + chunk + (args[i] || ""), "");
}
format("Hello %%! I ate %% apples today.", "World", 44);
// "Hello World, I ate 44 apples today."
Pensé que dado que esto, como los más antiguos, en realidad no analiza las letras, también podría usar un solo token %%
. Esto tiene la ventaja de ser obvio y no dificultar el uso de un solo %
. Sin embargo, si necesita %%
por algún motivo, deberá reemplazarlo por sí mismo:
format("I love percentage signs! %%", "%%");
// "I love percentage signs! %%"
Agregaré mis propios descubrimientos que he encontrado desde que pregunté:
Lamentablemente, parece que Sprintf no maneja miles de formatos de separador como el formato de cadena de .NET.
Aprovechando las soluciones previamente sugeridas:
// First, checks if it isn''t implemented yet.
if (!String.prototype.format) {
String.prototype.format = function() {
var args = arguments;
return this.replace(/{(/d+)}/g, function(match, number) {
return typeof args[number] != ''undefined''
? args[number]
: match
;
});
};
}
"{0} is dead, but {1} is alive! {0} {2}".format("ASP", "ASP.NET")
salidas
ASP está muerto, pero ASP.NET está vivo! ASP {2}
Si prefieres no modificar el prototipo de String
:
if (!String.format) {
String.format = function(format) {
var args = Array.prototype.slice.call(arguments, 1);
return format.replace(/{(/d+)}/g, function(match, number) {
return typeof args[number] != ''undefined''
? args[number]
: match
;
});
};
}
Te da el mucho más familiar:
String.format(''{0} is dead, but {1} is alive! {0} {2}'', ''ASP'', ''ASP.NET'');
con el mismo resultado:
ASP está muerto, pero ASP.NET está vivo! ASP {2}
Aquí hay una implementación mínima de sprintf en JavaScript: solo hace "% s" y "% d", pero me queda espacio para que se amplíe. Es inútil para el OP, pero otras personas que tropiezan con este hilo proveniente de Google podrían beneficiarse de ello.
function sprintf() {
var args = arguments,
string = args[0],
i = 1;
return string.replace(/%((%)|s|d)/g, function (m) {
// m is the matched format, e.g. %s, %d
var val = null;
if (m[2]) {
val = m[2];
} else {
val = args[i];
// A switch statement so that the formatter can be extended. Default is %s
switch (m) {
case ''%d'':
val = parseFloat(val);
if (isNaN(val)) {
val = 0;
}
break;
}
i++;
}
return val;
});
}
Ejemplo:
alert(sprintf(''Latitude: %s, Longitude: %s, Count: %d'', 41.847, -87.661, ''two''));
// Expected output: Latitude: 41.847, Longitude: -87.661, Count: 0
En contraste con soluciones similares en respuestas anteriores, esta realiza todas las sustituciones de una sola vez , por lo que no reemplazará partes de valores reemplazados previamente.
Edición: A partir de ES6 se pueden usar cadenas de plantillas:
let soMany = 10;
console.log(`This is ${soMany} times easier!`);
// "This is 10 times easier!
Vea la answer de Kim a continuación para más detalles.
Respuesta original:
Prueba sprintf () para JavaScript .
Actualice Ok, si realmente desea hacer un método de formato simple por su cuenta, no haga los reemplazos sucesivamente, sino los haga simultáneamente.
Debido a que la mayoría de las otras propuestas que se mencionan fallan cuando una cadena de reemplazo de reemplazo anterior también contiene una secuencia de formato como esta:
"{0}{1}".format("{1}", "{0}")
Normalmente se espera que la salida sea {1}{0}
pero la salida real es {1}{1}
. Así que haz un reemplazo a la vez, como en la sugerencia de fearphage .
El proyecto PHPJS ha escrito implementaciones de JavaScript para muchas de las funciones de PHP. Puesto que la función sprintf()
PHP es básicamente la misma que la printf()
C, su implementación de JavaScript debería satisfacer sus necesidades.
Es divertido porque en realidad tiene su propia función de formato para el prototipo String
llamado formatUnicorn
. ¡Intentalo! Entra en la consola y escribe algo como:
"Hello, {name}, are you feeling {adjective}?".formatUnicorn({name:"Gabriel", adjective: "OK"});
Obtienes esta salida:
Hello, Gabriel, are you feeling OK?
¡Puedes usar objetos, arreglos y cadenas como argumentos! Obtuve su código y lo modifiqué para producir una nueva versión de String.prototype.format
:
String.prototype.formatUnicorn = String.prototype.formatUnicorn ||
function () {
"use strict";
var str = this.toString();
if (arguments.length) {
var t = typeof arguments[0];
var key;
var args = ("string" === t || "number" === t) ?
Array.prototype.slice.call(arguments)
: arguments[0];
for (key in args) {
str = str.replace(new RegExp("//{" + key + "//}", "gi"), args[key]);
}
}
return str;
};
Tenga en cuenta la llamada inteligente Array.prototype.slice.call(arguments)
: eso significa que si introduce argumentos que son cadenas o números, no un solo objeto de estilo JSON, obtendrá el comportamiento String.Format
C # casi en forma exacta.
"a{0}bcd{1}ef".formatUnicorn("foo", "bar"); // yields "aFOObcdBARef"
Esto se debe a que la Array
de Array
forzará todo lo que esté en los arguments
en una Array
, ya sea originalmente o no, y la key
será el índice (0, 1, 2 ...) de cada elemento del array forzado en una cadena (por ejemplo, "0", así que "//{0//}"
para su primer patrón de expresión regular).
Ordenado.
Los programadores de JavaScript pueden usar String.prototype.sprintf en https://github.com/ildar-shaimordanov/jsxt/blob/master/js/String.js . A continuación se muestra un ejemplo:
var d = new Date();
var dateStr = ''%02d:%02d:%02d''.sprintf(
d.getHours(),
d.getMinutes(),
d.getSeconds());
Me sorprende que nadie haya usado reduce
, esta es una función JavaScript nativa, concisa y potente.
ES6 (EcmaScript2015)
String.prototype.format = function() {
return [...arguments].reduce((p,c) => p.replace(/%s/,c), this);
};
console.log(''Is that a %s or a %s?... No, it/'s %s!''.format(''plane'', ''bird'', ''SOman''));
<ES6
function interpolate(theString, argumentArray) {
var regex = /%s/;
var _r=function(p,c){return p.replace(regex,c);}
return argumentArray.reduce(_r, theString);
}
interpolate("%s, %s and %s", ["Me", "myself", "I"]); // "Me, myself and I"
Cómo funciona:
reduce aplica una función contra un acumulador y cada elemento de la matriz (de izquierda a derecha) para reducirla a un solo valor.
var _r= function(p,c){return p.replace(/%s/,c)};
console.log(
["a", "b", "c"].reduce(_r, "[%s], [%s] and [%s]") + ''/n'',
[1, 2, 3].reduce(_r, "%s+%s=%s") + ''/n'',
["cool", 1337, "stuff"].reduce(_r, "%s %s %s")
);
Muy elegante:
String.prototype.format = function (){
var args = arguments;
return this.replace(//{/{|/}/}|/{(/d+)/}/g, function (curlyBrack, index) {
return ((curlyBrack == "{{") ? "{" : ((curlyBrack == "}}") ? "}" : args[index]));
});
};
// Usage:
"{0}{1}".format("{1}", "{0}")
El crédito va a (Enlace roto) https://gist.github.com/0i0/1519811
Para aquellos a quienes les gusta Node.JS y su función util.format
, simplemente lo util.format
en su formulario de vainilla JavaScript (con solo las funciones que usa util.format):
exports = {};
function isString(arg) {
return typeof arg === ''string'';
}
function isNull(arg) {
return arg === null;
}
function isObject(arg) {
return typeof arg === ''object'' && arg !== null;
}
function isBoolean(arg) {
return typeof arg === ''boolean'';
}
function isUndefined(arg) {
return arg === void 0;
}
function stylizeNoColor(str, styleType) {
return str;
}
function stylizeWithColor(str, styleType) {
var style = inspect.styles[styleType];
if (style) {
return ''/u001b['' + inspect.colors[style][0] + ''m'' + str +
''/u001b['' + inspect.colors[style][3] + ''m'';
} else {
return str;
}
}
function isFunction(arg) {
return typeof arg === ''function'';
}
function isNumber(arg) {
return typeof arg === ''number'';
}
function isSymbol(arg) {
return typeof arg === ''symbol'';
}
function formatPrimitive(ctx, value) {
if (isUndefined(value))
return ctx.stylize(''undefined'', ''undefined'');
if (isString(value)) {
var simple = ''/''' + JSON.stringify(value).replace(/^"|"$/g, '''')
.replace(/''/g, "//'")
.replace(///"/g, ''"'') + ''/''';
return ctx.stylize(simple, ''string'');
}
if (isNumber(value)) {
// Format -0 as ''-0''. Strict equality won''t distinguish 0 from -0,
// so instead we use the fact that 1 / -0 < 0 whereas 1 / 0 > 0 .
if (value === 0 && 1 / value < 0)
return ctx.stylize(''-0'', ''number'');
return ctx.stylize('''' + value, ''number'');
}
if (isBoolean(value))
return ctx.stylize('''' + value, ''boolean'');
// For some reason typeof null is "object", so special case here.
if (isNull(value))
return ctx.stylize(''null'', ''null'');
// es6 symbol primitive
if (isSymbol(value))
return ctx.stylize(value.toString(), ''symbol'');
}
function arrayToHash(array) {
var hash = {};
array.forEach(function (val, idx) {
hash[val] = true;
});
return hash;
}
function objectToString(o) {
return Object.prototype.toString.call(o);
}
function isDate(d) {
return isObject(d) && objectToString(d) === ''[object Date]'';
}
function isError(e) {
return isObject(e) &&
(objectToString(e) === ''[object Error]'' || e instanceof Error);
}
function isRegExp(re) {
return isObject(re) && objectToString(re) === ''[object RegExp]'';
}
function formatError(value) {
return ''['' + Error.prototype.toString.call(value) + '']'';
}
function formatPrimitiveNoColor(ctx, value) {
var stylize = ctx.stylize;
ctx.stylize = stylizeNoColor;
var str = formatPrimitive(ctx, value);
ctx.stylize = stylize;
return str;
}
function isArray(ar) {
return Array.isArray(ar);
}
function hasOwnProperty(obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop);
}
function formatProperty(ctx, value, recurseTimes, visibleKeys, key, array) {
var name, str, desc;
desc = Object.getOwnPropertyDescriptor(value, key) || {value: value[key]};
if (desc.get) {
if (desc.set) {
str = ctx.stylize(''[Getter/Setter]'', ''special'');
} else {
str = ctx.stylize(''[Getter]'', ''special'');
}
} else {
if (desc.set) {
str = ctx.stylize(''[Setter]'', ''special'');
}
}
if (!hasOwnProperty(visibleKeys, key)) {
name = ''['' + key + '']'';
}
if (!str) {
if (ctx.seen.indexOf(desc.value) < 0) {
if (isNull(recurseTimes)) {
str = formatValue(ctx, desc.value, null);
} else {
str = formatValue(ctx, desc.value, recurseTimes - 1);
}
if (str.indexOf(''/n'') > -1) {
if (array) {
str = str.split(''/n'').map(function (line) {
return '' '' + line;
}).join(''/n'').substr(2);
} else {
str = ''/n'' + str.split(''/n'').map(function (line) {
return '' '' + line;
}).join(''/n'');
}
}
} else {
str = ctx.stylize(''[Circular]'', ''special'');
}
}
if (isUndefined(name)) {
if (array && key.match(/^/d+$/)) {
return str;
}
name = JSON.stringify('''' + key);
if (name.match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)) {
name = name.substr(1, name.length - 2);
name = ctx.stylize(name, ''name'');
} else {
name = name.replace(/''/g, "//'")
.replace(///"/g, ''"'')
.replace(/(^"|"$)/g, "''")
.replace(//////g, ''//');
name = ctx.stylize(name, ''string'');
}
}
return name + '': '' + str;
}
function formatArray(ctx, value, recurseTimes, visibleKeys, keys) {
var output = [];
for (var i = 0, l = value.length; i < l; ++i) {
if (hasOwnProperty(value, String(i))) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
String(i), true));
} else {
output.push('''');
}
}
keys.forEach(function (key) {
if (!key.match(/^/d+$/)) {
output.push(formatProperty(ctx, value, recurseTimes, visibleKeys,
key, true));
}
});
return output;
}
function reduceToSingleString(output, base, braces) {
var length = output.reduce(function (prev, cur) {
return prev + cur.replace(//u001b/[/d/d?m/g, '''').length + 1;
}, 0);
if (length > 60) {
return braces[0] +
(base === '''' ? '''' : base + ''/n '') +
'' '' +
output.join('',/n '') +
'' '' +
braces[1];
}
return braces[0] + base + '' '' + output.join('', '') + '' '' + braces[1];
}
function formatValue(ctx, value, recurseTimes) {
// Provide a hook for user-specified inspect functions.
// Check that value is an object with an inspect function on it
if (ctx.customInspect &&
value &&
isFunction(value.inspect) &&
// Filter out the util module, it''s inspect function is special
value.inspect !== exports.inspect &&
// Also filter out any prototype objects using the circular check.
!(value.constructor && value.constructor.prototype === value)) {
var ret = value.inspect(recurseTimes, ctx);
if (!isString(ret)) {
ret = formatValue(ctx, ret, recurseTimes);
}
return ret;
}
// Primitive types cannot have properties
var primitive = formatPrimitive(ctx, value);
if (primitive) {
return primitive;
}
// Look up the keys of the object.
var keys = Object.keys(value);
var visibleKeys = arrayToHash(keys);
if (ctx.showHidden) {
keys = Object.getOwnPropertyNames(value);
}
// This could be a boxed primitive (new String(), etc.), check valueOf()
// NOTE: Avoid calling `valueOf` on `Date` instance because it will return
// a number which, when object has some additional user-stored `keys`,
// will be printed out.
var formatted;
var raw = value;
try {
// the .valueOf() call can fail for a multitude of reasons
if (!isDate(value))
raw = value.valueOf();
} catch (e) {
// ignore...
}
if (isString(raw)) {
// for boxed Strings, we have to remove the 0-n indexed entries,
// since they just noisey up the output and are redundant
keys = keys.filter(function (key) {
return !(key >= 0 && key < raw.length);
});
}
// Some type of object without properties can be shortcutted.
if (keys.length === 0) {
if (isFunction(value)) {
var name = value.name ? '': '' + value.name : '''';
return ctx.stylize(''[Function'' + name + '']'', ''special'');
}
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), ''regexp'');
}
if (isDate(value)) {
return ctx.stylize(Date.prototype.toString.call(value), ''date'');
}
if (isError(value)) {
return formatError(value);
}
// now check the `raw` value to handle boxed primitives
if (isString(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize(''[String: '' + formatted + '']'', ''string'');
}
if (isNumber(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize(''[Number: '' + formatted + '']'', ''number'');
}
if (isBoolean(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
return ctx.stylize(''[Boolean: '' + formatted + '']'', ''boolean'');
}
}
var base = '''', array = false, braces = [''{'', ''}''];
// Make Array say that they are Array
if (isArray(value)) {
array = true;
braces = [''['', '']''];
}
// Make functions say that they are functions
if (isFunction(value)) {
var n = value.name ? '': '' + value.name : '''';
base = '' [Function'' + n + '']'';
}
// Make RegExps say that they are RegExps
if (isRegExp(value)) {
base = '' '' + RegExp.prototype.toString.call(value);
}
// Make dates with properties first say the date
if (isDate(value)) {
base = '' '' + Date.prototype.toUTCString.call(value);
}
// Make error with message first say the error
if (isError(value)) {
base = '' '' + formatError(value);
}
// Make boxed primitive Strings look like such
if (isString(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = '' '' + ''[String: '' + formatted + '']'';
}
// Make boxed primitive Numbers look like such
if (isNumber(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = '' '' + ''[Number: '' + formatted + '']'';
}
// Make boxed primitive Booleans look like such
if (isBoolean(raw)) {
formatted = formatPrimitiveNoColor(ctx, raw);
base = '' '' + ''[Boolean: '' + formatted + '']'';
}
if (keys.length === 0 && (!array || value.length === 0)) {
return braces[0] + base + braces[1];
}
if (recurseTimes < 0) {
if (isRegExp(value)) {
return ctx.stylize(RegExp.prototype.toString.call(value), ''regexp'');
} else {
return ctx.stylize(''[Object]'', ''special'');
}
}
ctx.seen.push(value);
var output;
if (array) {
output = formatArray(ctx, value, recurseTimes, visibleKeys, keys);
} else {
output = keys.map(function (key) {
return formatProperty(ctx, value, recurseTimes, visibleKeys, key, array);
});
}
ctx.seen.pop();
return reduceToSingleString(output, base, braces);
}
function inspect(obj, opts) {
// default options
var ctx = {
seen: [],
stylize: stylizeNoColor
};
// legacy...
if (arguments.length >= 3) ctx.depth = arguments[2];
if (arguments.length >= 4) ctx.colors = arguments[3];
if (isBoolean(opts)) {
// legacy...
ctx.showHidden = opts;
} else if (opts) {
// got an "options" object
exports._extend(ctx, opts);
}
// set default options
if (isUndefined(ctx.showHidden)) ctx.showHidden = false;
if (isUndefined(ctx.depth)) ctx.depth = 2;
if (isUndefined(ctx.colors)) ctx.colors = false;
if (isUndefined(ctx.customInspect)) ctx.customInspect = true;
if (ctx.colors) ctx.stylize = stylizeWithColor;
return formatValue(ctx, obj, ctx.depth);
}
exports.inspect = inspect;
// http://en.wikipedia.org/wiki/ANSI_escape_code#graphics
inspect.colors = {
''bold'': [1, 22],
''italic'': [3, 23],
''underline'': [4, 24],
''inverse'': [7, 27],
''white'': [37, 39],
''grey'': [90, 39],
''black'': [30, 39],
''blue'': [34, 39],
''cyan'': [36, 39],
''green'': [32, 39],
''magenta'': [35, 39],
''red'': [31, 39],
''yellow'': [33, 39]
};
// Don''t use ''blue'' not visible on cmd.exe
inspect.styles = {
''special'': ''cyan'',
''number'': ''yellow'',
''boolean'': ''yellow'',
''undefined'': ''grey'',
''null'': ''bold'',
''string'': ''green'',
''symbol'': ''green'',
''date'': ''magenta'',
// "name": intentionally not styling
''regexp'': ''red''
};
var formatRegExp = /%[sdj%]/g;
exports.format = function (f) {
if (!isString(f)) {
var objects = [];
for (var j = 0; j < arguments.length; j++) {
objects.push(inspect(arguments[j]));
}
return objects.join('' '');
}
var i = 1;
var args = arguments;
var len = args.length;
var str = String(f).replace(formatRegExp, function (x) {
if (x === ''%%'') return ''%'';
if (i >= len) return x;
switch (x) {
case ''%s'':
return String(args[i++]);
case ''%d'':
return Number(args[i++]);
case ''%j'':
try {
return JSON.stringify(args[i++]);
} catch (_) {
return ''[Circular]'';
}
default:
return x;
}
});
for (var x = args[i]; i < len; x = args[++i]) {
if (isNull(x) || !isObject(x)) {
str += '' '' + x;
} else {
str += '' '' + inspect(x);
}
}
return str;
};
Recolectado de: https://github.com/joyent/node/blob/master/lib/util.js
Para los usuarios de Node.js hay util.format
que tiene una funcionalidad similar a printf:
util.format("%s world", "Hello")
Quiero compartir mi solución para el ''problema''. No he reinventado la rueda pero intento encontrar una solución basada en lo que JavaScript ya hace. La ventaja es que obtienes todas las conversiones implícitas de forma gratuita. Establecer la propiedad prototipo $ de String proporciona una sintaxis muy agradable y compacta (ver ejemplos a continuación) Tal vez no sea la forma más eficiente, pero en la mayoría de los casos tratar con la salida no tiene que ser super optimizado.
String.form = function(str, arr) {
var i = -1;
function callback(exp, p0, p1, p2, p3, p4) {
if (exp==''%%'') return ''%'';
if (arr[++i]===undefined) return undefined;
exp = p2 ? parseInt(p2.substr(1)) : undefined;
var base = p3 ? parseInt(p3.substr(1)) : undefined;
var val;
switch (p4) {
case ''s'': val = arr[i]; break;
case ''c'': val = arr[i][0]; break;
case ''f'': val = parseFloat(arr[i]).toFixed(exp); break;
case ''p'': val = parseFloat(arr[i]).toPrecision(exp); break;
case ''e'': val = parseFloat(arr[i]).toExponential(exp); break;
case ''x'': val = parseInt(arr[i]).toString(base?base:16); break;
case ''d'': val = parseFloat(parseInt(arr[i], base?base:10).toPrecision(exp)).toFixed(0); break;
}
val = typeof(val)==''object'' ? JSON.stringify(val) : val.toString(base);
var sz = parseInt(p1); /* padding size */
var ch = p1 && p1[0]==''0'' ? ''0'' : '' ''; /* isnull? */
while (val.length<sz) val = p0 !== undefined ? val+ch : ch+val; /* isminus? */
return val;
}
var regex = /%(-)?(0?[0-9]+)?([.][0-9]+)?([#][0-9]+)?([scfpexd%])/g;
return str.replace(regex, callback);
}
String.prototype.$ = function() {
return String.form(this, Array.prototype.slice.call(arguments));
}
Aquí están algunos ejemplos:
String.format("%s %s", [ "This is a string", 11 ])
console.log("%s %s".$("This is a string", 11))
var arr = [ "12.3", 13.6 ]; console.log("Array: %s".$(arr));
var obj = { test:"test", id:12 }; console.log("Object: %s".$(obj));
console.log("%c", "Test");
console.log("%5d".$(12)); // '' 12''
console.log("%05d".$(12)); // ''00012''
console.log("%-5d".$(12)); // ''12 ''
console.log("%5.2d".$(123)); // '' 120''
console.log("%5.2f".$(1.1)); // '' 1.10''
console.log("%10.2e".$(1.1)); // '' 1.10e+0''
console.log("%5.3p".$(1.12345)); // '' 1.12''
console.log("%5x".$(45054)); // '' affe''
console.log("%20#2x".$("45054")); // '' 1010111111111110''
console.log("%6#2d".$("111")); // '' 7''
console.log("%6#16d".$("affe")); // '' 45054''
Tengo una solución muy cercana a la de Peter, pero se trata del caso del número y el objeto.
if (!String.prototype.format) {
String.prototype.format = function() {
var args;
args = arguments;
if (args.length === 1 && args[0] !== null && typeof args[0] === ''object'') {
args = args[0];
}
return this.replace(/{([^}]*)}/g, function(match, key) {
return (typeof args[key] !== "undefined" ? args[key] : match);
});
};
}
Tal vez podría ser incluso mejor tratar con los casos más profundos, pero para mis necesidades esto está bien.
"This is an example from {name}".format({name:"Blaine"});
"This is an example from {0}".format("Blaine");
PD: esta función es genial si está utilizando traducciones en marcos de plantillas como AngularJS :
<h1> {{(''hello-message''|translate).format(user)}} <h1>
<h1> {{(''hello-by-name''|translate).format( user ? user.name : ''You'' )}} <h1>
Donde el en.json es algo como
{
"hello-message": "Hello {name}, welcome.",
"hello-by-name": "Hello {0}, welcome."
}
Una versión ligeramente diferente, la que prefiero (esta usa {tokens {xxx} en lugar de {0} argumentos numerados, esto es mucho más auto-documentado y se adapta mucho mejor a la localización):
String.prototype.format = function(tokens) {
var formatted = this;
for (var token in tokens)
if (tokens.hasOwnProperty(token))
formatted = formatted.replace(RegExp("{" + token + "}", "g"), tokens[token]);
return formatted;
};
Una variación sería:
var formatted = l(this);
que llama primero a una función de localización l ().
Uso una pequeña biblioteca llamada String.format para JavaScript que admite la mayoría de las capacidades de cadena de formato (incluido el formato de números y fechas), y utiliza la sintaxis .NET. El script en sí es más pequeño que 4 kB, por lo que no genera mucha sobrecarga.
Yo uso esta función simple:
String.prototype.format = function() {
var formatted = this;
for( var arg in arguments ) {
formatted = formatted.replace("{" + arg + "}", arguments[arg]);
}
return formatted;
};
Eso es muy similar a string.format:
"{0} is dead, but {1} is alive!".format("ASP", "ASP.NET")
Yo uso este:
String.prototype.format = function() {
var newStr = this, i = 0;
while (/%s/.test(newStr))
newStr = newStr.replace("%s", arguments[i++])
return newStr;
}
Entonces lo llamo:
"<h1>%s</h1><p>%s</p>".format("Header", "Just a test!");
jsxt, zippo
Esta opción se ajusta mejor.
String.prototype.format = function() {
var formatted = this;
for (var i = 0; i < arguments.length; i++) {
var regexp = new RegExp(''//{''+i+''//}'', ''gi'');
formatted = formatted.replace(regexp, arguments[i]);
}
return formatted;
};
Con esta opción puedo reemplazar cadenas como estas:
''The {0} is dead. Don/'t code {0}. Code {1} that is open source!''.format(''ASP'', ''PHP'');
Con su código, el segundo {0} no sería reemplazado. ;)
En caso de que alguien necesite una función para evitar el alcance global contaminante, aquí está la función que hace lo mismo:
function _format (str, arr) {
return str.replace(/{(/d+)}/g, function (match, number) {
return typeof arr[number] != ''undefined'' ? arr[number] : match;
});
};
Para el formateo básico:
var template = jQuery.validator.format("{0} is not a valid value");
var result = template("abc");
Hay "sprintf" para JavaScript que puede encontrar en http://www.webtoolkit.info/javascript-sprintf.html .
No vi pyformat en la lista, así que pensé en tirarlo:
console.log(pyformat( ''The {} {} jumped over the {}''
, [''brown'' ,''fox'' ,''foobar'']
))
console.log(pyformat(''The {0} {1} jumped over the {1}''
, [''brown'' ,''fox'' ,''foobar'']
))
console.log(pyformat(''The {color} {animal} jumped over the {thing}''
, [] ,{color: ''brown'' ,animal: ''fox'' ,thing: ''foobaz''}
))
No vi la String.format
variante:
String.format = function (string) {
var args = Array.prototype.slice.call(arguments, 1, arguments.length);
return string.replace(/{(/d+)}/g, function (match, number) {
return typeof args[number] != "undefined" ? args[number] : match;
});
};
Tengo un formateador un poco más largo para JavaScript here ...
Puedes hacer el formateo de varias maneras:
-
String.format(input, args0, arg1, ...)
-
String.format(input, obj)
-
"literal".format(arg0, arg1, ...)
-
"literal".format(obj)
Además, si ha dicho un ObjectBase.prototype.format (como con DateJS ), lo utilizará.
Ejemplos ...
var input = "numbered args ({0}-{1}-{2}-{3})";
console.log(String.format(input, "first", 2, new Date()));
//Outputs "numbered args (first-2-Thu May 31 2012...Time)-{3})"
console.log(input.format("first", 2, new Date()));
//Outputs "numbered args(first-2-Thu May 31 2012...Time)-{3})"
console.log(input.format(
"object properties ({first}-{second}-{third:yyyy-MM-dd}-{fourth})"
,{
''first'':''first''
,''second'':2
,''third'':new Date() //assumes Date.prototype.format method
}
));
//Outputs "object properties (first-2-2012-05-31-{3})"
También he creado un alias con .asFormat y tengo alguna detección implementada en caso de que ya exista un string.format (como con MS Ajax Toolkit (odio esa biblioteca).
/**
* Format string by replacing placeholders with value from element with
* corresponsing index in `replacementArray`.
* Replaces are made simultaneously, so that replacement values like
* ''{1}'' will not mess up the function.
*
* Example 1:
* (''{2} {1} {0}'', [''three'', ''two'' ,''one'']) -> ''one two three''
*
* Example 2:
* (''{0}{1}'', [''{1}'', ''{0}'']) -> ''{1}{0}''
*/
function stringFormat(formatString, replacementArray) {
return formatString.replace(
//{(/d+)/}/g, // Matches placeholders, e.g. ''{1}''
function formatStringReplacer(match, placeholderIndex) {
// Convert String to Number
placeholderIndex = Number(placeholderIndex);
// Make sure that index is within replacement array bounds
if (placeholderIndex < 0 ||
placeholderIndex > replacementArray.length - 1
) {
return placeholderIndex;
}
// Replace placeholder with value from replacement array
return replacementArray[placeholderIndex];
}
);
}