Last active
December 19, 2015 16:38
-
-
Save gooocho/5984973 to your computer and use it in GitHub Desktop.
underscore.stringのsprintf
http://www.diveintojavascript.com/projects/javascript-sprintf
も
qiitaで紹介されてたsprintf
http://homepage3.nifty.com/aya_js/js_sub/sprintf.js
も仕様
http://linuxjm.sourceforge.jp/html/LDP_man-pages/man3/printf.3.html
を全然満たしてないので自分で作った
あとでもう少し直す/テスト書く
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var sprintf = (function(isModerate) { | |
function lpad(str, len, ch) { | |
str = String(str); | |
len = ~~len; | |
ch = String(ch); | |
if (str.length > len) { | |
return str; | |
} | |
return ((new Array(len + 1).join(ch)) + str).slice(-len); | |
} | |
function rpad(str, len, ch) { | |
str = String(str); | |
len = ~~len; | |
ch = String(ch); | |
if (str.length > len) { | |
return str; | |
} | |
return (str + (new Array(len + 1).join(ch))).slice(0, len); | |
} | |
function repeat(ch, len) { | |
len = ~~len; | |
if (len + 1 >= 0) { | |
return new Array(len + 1).join(ch); | |
} else { | |
return ''; | |
} | |
} | |
function Format(argList, | |
flags, | |
minimumFieldWidth, | |
precision, | |
conversionSpecifer) { | |
this.flags = new FormatFlags(flags); | |
this.precision = | |
precision === '.' ? 0 : | |
precision ? Number(precision.slice(1)) : | |
null; | |
this.conversionSpecifer = conversionSpecifer; | |
if (minimumFieldWidth === '*') { | |
var firstArg = argList.splice(0, 1); | |
this.minimumFieldWidth = Math.abs(firstArg | 0); | |
if (firstArg < 0) { | |
this.flags.leftJustified = true; | |
} | |
} else { | |
this.minimumFieldWidth = | |
minimumFieldWidth ? Number(minimumFieldWidth) : | |
null; | |
} | |
} | |
/** | |
* @private | |
* @constructor | |
*/ | |
function FormatFlags(flags) { | |
flags = flags || ''; | |
this.alternativeForm = flags.indexOf('#') >= 0; | |
this.leftJustified = flags.indexOf('-') >= 0; | |
this.zeroPadding = !this.leftJustified && flags.indexOf('0') >= 0; | |
this.alwaysSigned = flags.indexOf('+') >= 0; | |
this.spaceSigned = !this.alwaysSigned && flags.indexOf(' ') >= 0; | |
this.paddingChar = this.zeroPadding ? '0' : ' '; | |
this.positiveSignChar = this.alwaysSigned ? '+' : | |
this.spaceSigned ? ' ' : | |
''; | |
} | |
function toInteger(signedValue, format, cs) { | |
var prefix = { | |
d: '', | |
i: '', | |
o: '0', | |
u: '', | |
x: '0x', | |
X: '0X' | |
}; | |
var base = { | |
d: 10, | |
i: 10, | |
o: 8, | |
u: 10, | |
x: 16, | |
X: 16 | |
}; | |
var unsignedValue = Math.abs(signedValue); | |
var unsignedValueString = unsignedValue.toString(base[cs]); | |
if (cs === cs.toUpperCase()) { | |
unsignedValueString = unsignedValueString.toUpperCase(); | |
} | |
if (format.precision !== null) { | |
if (format.precision === 0 && unsignedValue === 0) { | |
unsignedValueString = repeat( | |
'0', | |
format.precision - unsignedValueString.length | |
); | |
} else { | |
unsignedValueString = repeat( | |
'0', | |
format.precision - unsignedValueString.length | |
) + unsignedValueString; | |
} | |
format.flags.zeroPadding = null; | |
} | |
unsignedValueString = format.flags.alternativeForm ? | |
prefix[cs] + unsignedValueString : | |
unsignedValueString; | |
var signChar = signedValue < 0 ? '-' : format.flags.positiveSignChar; | |
var paddingChars = repeat( | |
format.flags.paddingChar, | |
format.minimumFieldWidth - signChar.length - unsignedValueString.length | |
); | |
if (format.flags.leftJustified) { | |
return signChar + unsignedValueString + paddingChars; | |
} else if (format.flags.zeroPadding) { | |
return signChar + paddingChars + unsignedValueString; | |
} else { | |
return paddingChars + signChar + unsignedValueString; | |
} | |
} | |
function toFloat(signedValue, format, cs, parseFunc) { | |
if (format.precision === null) { | |
format.precision = 6; | |
} | |
var parseResult = parseFunc(signedValue, format.precision); | |
var signChar = signedValue < 0 ? '-' : format.flags.positiveSignChar; | |
var integerPart = parseResult.integerPart; | |
var period = (!format.flags.alternativeForm && format.precision === 0) ? '' : '.'; | |
var fractionPart = parseResult.fractionPart; | |
var exponentialSign = parseResult.exponentialSign; | |
var exponentIndicator = { | |
e: 'e', | |
E: 'E', | |
f: '', | |
F: '' | |
}[cs]; | |
var exponentialPart = parseResult.exponentialPart; | |
var fractionPaddingChars = repeat( | |
'0', | |
format.precision - fractionPart.length | |
); | |
var paddingChars = repeat( | |
format.flags.paddingChar, | |
format.minimumFieldWidth - | |
signChar.length - | |
integerPart.length - | |
period.length - | |
fractionPart.length - | |
exponentIndicator.length - | |
exponentialSign.length - | |
exponentialPart.length - | |
fractionPaddingChars.length | |
); | |
if (format.flags.leftJustified) { | |
return [ | |
signChar, | |
integerPart, | |
period, | |
fractionPart, | |
fractionPaddingChars, | |
exponentIndicator, | |
exponentialSign, | |
exponentialPart, | |
paddingChars | |
].join(''); | |
} else if (format.flags.zeroPadding) { | |
return [ | |
signChar, | |
paddingChars, | |
integerPart, | |
period, | |
fractionPart, | |
fractionPaddingChars, | |
exponentIndicator, | |
exponentialSign, | |
exponentialPart | |
].join(''); | |
} else { | |
return [ | |
paddingChars, | |
signChar, | |
integerPart, | |
period, | |
fractionPart, | |
fractionPaddingChars, | |
exponentIndicator, | |
exponentialSign, | |
exponentialPart | |
].join(''); | |
} | |
} | |
function eParse(signedValue, precision) { | |
var exponentialReg = /^-?(\d+)(?:\.(\d+))?e([+-])(\d+)$/; | |
var exponentialForm = signedValue.toExponential(20); | |
var exec = exponentialReg.exec(exponentialForm); | |
if (exec) { | |
exec[2] = exec[2] || ''; | |
// finite number | |
return { | |
integerPart: exec[1], | |
fractionPart: rpad(exec[2], precision, '0').substr(0, precision), | |
exponentialSign: exec[3], | |
exponentialPart: lpad(exec[4], 3, '0') | |
}; | |
} else { | |
// NaN, Infinity | |
return { | |
integerPart: '1', | |
fractionPart: ('#' + String(Math.abs(signedValue)).substr(0, 3).toUpperCase()).substr(0, precision), | |
exponentialSign: '+', | |
exponentialPart: '000' | |
}; | |
} | |
} | |
function fParse(signedValue, precision) { | |
var exponentialReg = /^-?(\d+)(?:\.(\d+))?e([+-])(\d+)$/; | |
var exponentialForm = signedValue.toExponential(20); | |
var exec = exponentialReg.exec(exponentialForm); | |
if (exec) { | |
exec[2] = exec[2] || ''; | |
// finite number | |
return { | |
integerPart: (exec[3] === '+') ? | |
exec[1] + rpad(exec[2], exec[4], '0').substr(0, exec[4]) : | |
'0', | |
fractionPart: (exec[3] === '+') ? | |
(exec[2].substr(exec[4])).substr(0, precision) : | |
(lpad(exec[1], exec[4], '0').substr(-exec[4]) + exec[2]).substr(0, precision), | |
exponentialSign: '', | |
exponentialPart: '' | |
}; | |
} else { | |
// NaN, Infinity | |
return { | |
integerPart: '1', | |
fractionPart: ('#' + String(Math.abs(signedValue)).substr(0, 3).toUpperCase()).substr(0, precision), | |
exponentialSign: '', | |
exponentialPart: '' | |
}; | |
} | |
} | |
/** | |
* @private | |
* @constructor | |
*/ | |
function formatString(arg, format) { | |
var cs = format.conversionSpecifer; | |
switch (cs) { | |
case 'd': | |
case 'i': | |
var signedValue = arg | 0; | |
return toInteger(signedValue, format, cs); | |
case 'o': | |
case 'u': | |
case 'x': | |
case 'X': | |
var signedValue = arg >>> 0; | |
return toInteger(signedValue, format, cs); | |
case 'e': | |
case 'E': | |
format.flags.paddingChar = ' '; | |
format.flags.positiveSignChar = ''; | |
format.flags.zeroPadding = false; | |
return toFloat(arg, format, cs, eParse); | |
case 'f': | |
case 'F': | |
return toFloat(arg, format, cs, fParse); | |
case 'c': | |
var resultChar; | |
if (typeof arg === 'string') { | |
resultChar = arg[0]; | |
} else if (typeof arg === 'number') { | |
resultChar = String.fromCharCode(arg); | |
} | |
if (format.minimumFieldWidth !== null) { | |
var paddingChars = repeat(' ', format.minimumFieldWidth - 1); | |
if (format.flags.leftJustified) { | |
return resultChar + paddingChars; | |
} else { | |
return paddingChars + resultChar; | |
} | |
} else { | |
return resultChar; | |
} | |
case 's': | |
if (format.minimumFieldWidth !== null) { | |
var paddingChars = repeat(' ', format.minimumFieldWidth - arg.length); | |
if (format.flags.leftJustified) { | |
return arg + paddingChars; | |
} else { | |
return paddingChars + arg; | |
} | |
} else { | |
return String(arg); | |
} | |
case '%': | |
return '%'; | |
} | |
} | |
return function(str) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
var workingStr = str; | |
var result = ''; | |
// ignoreing "length modifers" | |
var reg = /%([-+ #0]*)?([*]|\d+)?(\.\d*)?(.)/g; | |
var exec; | |
while (true) { | |
workingStr = workingStr.slice(reg.lastIndex); | |
reg.lastIndex = 0; | |
if (exec = reg.exec(workingStr)) { | |
// argsに対して副作用がある. | |
var format = new Format(args, exec[1], exec[2], exec[3], exec[4]); | |
var arg = args.splice(0, 1)[0]; | |
result += workingStr.slice(0, reg.lastIndex - exec[0].length); | |
result += formatString(arg, format); | |
} else { | |
break; | |
} | |
} | |
return result + workingStr; | |
}; | |
})(); | |
sprintf('%-5d____%10.3e____%0#*X', 10, 123.4567, 4, 256); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment