Skip to content

Instantly share code, notes, and snippets.

@gooocho
Last active December 19, 2015 16:38
Show Gist options
  • Save gooocho/5984973 to your computer and use it in GitHub Desktop.
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 を全然満たしてないので自分で作った あとでもう少し直す/テスト書く
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