速くてコンパクトな JavaScript 用の sprintf の実装
[javascript][sprintf] で検索してたどり着く方が多いようなので、uupaa-0.7.js から切り出して張ってみます。
/*!{id:"uupaa.js",ver:0.7,license:"MIT",author:"uupaa.js@gmail.com"}*/ window.sprintf || (function() { var _BITS = { i: 0x8011, d: 0x8011, u: 0x8021, o: 0x8161, x: 0x8261, X: 0x9261, f: 0x92, c: 0x2800, s: 0x84 }, _PARSE = /%(?:(\d+)\$)?(#|0)?(\d+)?(?:\.(\d+))?(l)?([%iduoxXfcs])/g; window.sprintf = _sprintf; function _sprintf(format) { function _fmt(m, argidx, flag, width, prec, size, types) { if (types === "%") { return "%"; } var v = "", w = _BITS[types], overflow, pad; idx = argidx ? parseInt(argidx) : next++; w & 0x400 || (v = (av[idx] === void 0) ? "" : av[idx]); w & 3 && (v = (w & 1) ? parseInt(v) : parseFloat(v), v = isNaN(v) ? "": v); w & 4 && (v = ((types === "s" ? v : types) || "").toString()); w & 0x20 && (v = (v >= 0) ? v : v % 0x100000000 + 0x100000000); w & 0x300 && (v = v.toString(w & 0x100 ? 8 : 16)); w & 0x40 && (flag === "#") && (v = ((w & 0x100) ? "0" : "0x") + v); w & 0x80 && prec && (v = (w & 2) ? v.toFixed(prec) : v.slice(0, prec)); w & 0x6000 && (overflow = (typeof v !== "number" || v < 0)); w & 0x2000 && (v = overflow ? "" : String.fromCharCode(v)); w & 0x8000 && (flag = (flag === "0") ? "" : flag); v = w & 0x1000 ? v.toString().toUpperCase() : v.toString(); if (!(w & 0x800 || width === void 0 || v.length >= width)) { pad = Array(width - v.length + 1).join(!flag ? " " : flag === "#" ? " " : flag); v = ((w & 0x10 && flag === "0") && !v.indexOf("-")) ? ("-" + pad + v.slice(1)) : (pad + v); } return v; } var next = 1, idx = 0, av = arguments; return format.replace(_PARSE, _fmt); } })();
PHP の sprintf の仕様をベースにしています。http://jp2.php.net/manual/ja/function.sprintf.php
また、uupaa-0.7.js の uu.fmt() の機能から、"%A" (PRINTABLE), "%j" (JSON), "%r" (RGB), "%R" (RGBA), "%h" (#000000) を削っています。
ざっくりとした説明
format: %[arg-index$][flag][width][.precision][size]type arg-index: 引数の呼び出しと再利用, 数値: 数値とダラー("$")により引数を0から始まる番号で呼び出すことができます。 flag: 出力方法を指定します。 以下をサポートします。 "#": typeがo,x,Xなら文字列の先頭に"0","0x","0X"を追加します。 以下は非サポートです。 "-": 左詰で出力します。(非サポート) "+": 数値の前に符号を追加します。(非サポート) 空白: 数値が負なら"-"を、それ以外なら空白を出力します。(非サポート) width: 出力する最小文字数か数値の最低桁数を指定します。 以下をサポートします。 数値: 数値により最低限表示する桁数を指定できます。0で非表示になります。 数値や文字列の桁あわせに使用します。 以下は非サポートです。 "*": 引数で幅を指定します。(非サポート) precision: 出力する最大文字数か小数点以下の桁数を指定します。 以下をサポートします。 数値: 数値で、小数点以下の桁数や文字列の長さを指定できます。 precisionの前にはドット(".")が必要です。 typeがfなら小数点以下の桁数を指定します。 浮動小数点値は丸められる場合があります。0で小数点以下は非表示になります。 typeがsなら文字列の長さを指定します。 指定した長さ以上の文字は切り捨てられます。0で文字列全体が非表示になります。 以下は非サポートです。 "*": 引数で精度を指定します。(非サポート) size: デフォルト引数のサイズを指定します。 以下は非サポートです。 "l": long型に変更します。(非サポート) type: 変数の型を指定します。 以下をサポートします。 "i": 符号付き8進数値(signed octet number) "d": 符号付き10進数値(signed decimal number) "u": 符号無し10進数値(unsigned decimal number) "o": 符号無し8進数値(unsigned octet number) "x": 符号無し16進数値[小文字](unsigned hex number[lower case]) "X": 符号無し16進数値[大文字](unsigned hex number[upper case]) "f": 浮動小数点([-]dddd.dddd)(floating-point number) "c": 文字の数値表現(the character with that ASCII value) "s": 文字列(string) "%": パーセント記号("%")そのものを出力 以下は非サポートです。"egEG"は"f"で代用してください。 "e": 浮動小数点([-]d.dddde[+/-]dddd) "g": 浮動小数点("f","e"の結果でより短い方を出力する) "E": 浮動小数点([-]d.ddddE[+/-]dddd) "G": 浮動小数点("f","E"の結果でより短い方を出力する) "n": 出力済みの文字数 "p": ポインタ
速度的な
<!doctype html><html><head><title></title> <script src="xxx_sprintf.js"></script> <script> function perf(loop) { for (var i = 0; i < loop; ++i) { var n = 43951789; var u = -43951789; var c = 65; var d = 123.45678901234567890123456789; sprintf("%%c = '%c'<br />", c); sprintf("%%d = '%d'<br />", u); sprintf("%%u = '%u'<br />", n); sprintf("%%u = '%u'<br />", u); sprintf("%%f = '%010.10f'<br />", d); sprintf("%%o = '%o'<br />", n); sprintf("%%s = '%100.10s'<br />", "Ala-bala-portocala"); sprintf("%%x = '%x'<br />", n); sprintf("%%X = '%X'<br />", n); sprintf("<br />%4$s, %3$s, %1$s, %2$s", 'c', 'd', 'b', 'a'); } } var begin = +new Date; perf(1000); alert((+new Date - begin)); </script> </head><body></body></html>
[javascript][sprintf]でぐぐって、上位10以内のライブラリと速度を比較してみました。
- A: uupaa-0.7.js
- B: http://sourceforge.jp/projects/freshmeat_sprintf/
- C: http://www.onicos.com/staff/iz/amuse/javascript/expert/
A | B | C | |
IE6 | 657 | 1516 | 1671 |
IE8 | 562 | 1312 | 1203 |
Opera10 | 625 | 922 | 1141 |
Firefox3.5.3 | 381 | 597 | 390 |
Google Chrome4dev | 171 | 196 | 119 |
(単位ms)
見ろ、我軍は圧倒的でわ… あれ? Chrome4 で負けてる