我写了一个大小转换函数,它也接受人类可读的格式。
JS
const bitBase = 8;
const suffixes = {
bit: 'b',
b: 'B',
kb: 'KB',
mb: 'MB',
gb: 'GB',
tb: 'TB',
};
const multipliers = {
bit: {
toBitHr: 1,
toB: 1 / bitBase,
toKB: 1 / (bitBase * 1e3),
toMB: 1 / (bitBase * 1e6),
toGB: 1 / (bitBase * 1e9),
toTB: 1 / (bitBase * 1e12),
},
B: {
toBit: bitBase,
toBHr: 1,
toKB: 1 / 1e3,
toMB: 1 / 1e6,
toGB: 1 / 1e9,
toTB: 1 / 1e12,
},
KB: {
toBit: 1 / (bitBase * 1e3),
toB: 1e3,
toKBHr: 1,
toMB: 1 / 1e3,
toGB: 1 / 1e6,
toTB: 1 / 1e9,
},
MB: {
toBit: bitBase * 1e6,
toB: 1e6,
toKB: 1e3,
toMBHr: 1,
toGB: 1 / 1e3,
toTB: 1 / 1e6,
},
GB: {
toBit: bitBase * 1e9,
toB: 1e9,
toKB: 1e6,
toMB: 1e3,
toGBHr: 1,
toTB: 1 / 1e3,
},
TB: {
toBit: bitBase * 1e12,
toB: 1e12,
toKB: 1e9,
toMB: 1e6,
toGB: 1e3,
toTBHr: 1,
},
};
const round = (num, decimalPlaces) => {
const strNum = num.toString();
const isExp = strNum.includes('e');
if (isExp) {
return Number(num.toPrecision(decimalPlaces + 1));
}
return Number(
`${Math.round(Number(`${num}e${decimalPlaces}`))}e${decimalPlaces * -1}`,
);
};
function conv(
value,
hr,
rnd,
multiplier,
suffix,
) {
let val = value * multiplier;
if ((value * multiplier) > Number.MAX_SAFE_INTEGER) {
val = Number.MAX_SAFE_INTEGER;
}
if (val < Number.MIN_VALUE) val = 0;
if ((rnd || rnd === 0) && val < Number.MAX_SAFE_INTEGER) {
val = round(val, rnd);
}
if (hr) return `${val}${suffix}`;
return val;
}
const MemConv = (function _() {
return {
bit(value) {
return {
toBitHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.bit.toBitHr,
suffixes.bit,
);
},
toB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.bit.toB,
suffixes.b,
);
},
toKB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.bit.toKB,
suffixes.kb,
);
},
toMB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.bit.toMB,
suffixes.mb,
);
},
toGB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.bit.toGB,
suffixes.gb,
);
},
toTB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.bit.toTB,
suffixes.tb,
);
},
};
},
B(value) {
return {
toBit(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.B.toBit,
suffixes.bit,
);
},
toBHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.B.toBHr,
suffixes.b,
);
},
toKB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.B.toKB,
suffixes.kb,
);
},
toMB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.B.toMB,
suffixes.mb,
);
},
toGB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.B.toGB,
suffixes.gb,
);
},
toTB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.B.toTB,
suffixes.tb,
);
},
};
},
KB(value) {
return {
toBit(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.KB.toBit,
suffixes.bit,
);
},
toB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.KB.toB,
suffixes.b,
);
},
toKBHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.KB.toKBHr,
suffixes.kb,
);
},
toMB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.KB.toMB,
suffixes.mb,
);
},
toGB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.KB.toGB,
suffixes.gb,
);
},
toTB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.KB.toTB,
suffixes.tb,
);
},
};
},
MB(value) {
return {
toBit(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.MB.toBit,
suffixes.bit,
);
},
toB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.MB.toB,
suffixes.b,
);
},
toKB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.MB.toKB,
suffixes.kb,
);
},
toMBHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.MB.toMBHr,
suffixes.mb,
);
},
toGB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.MB.toGB,
suffixes.gb,
);
},
toTB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.MB.toTB,
suffixes.tb,
);
},
};
},
GB(value) {
return {
toBit(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.GB.toBit,
suffixes.bit,
);
},
toB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.GB.toB,
suffixes.b,
);
},
toKB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.GB.toKB,
suffixes.kb,
);
},
toMB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.GB.toMB,
suffixes.mb,
);
},
toGBHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.GB.toGBHr,
suffixes.gb,
);
},
toTB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.GB.toTB,
suffixes.tb,
);
},
};
},
TB(value) {
return {
toBit(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.TB.toBit,
suffixes.bit,
);
},
toB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.TB.toB,
suffixes.b,
);
},
toKB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.TB.toKB,
suffixes.kb,
);
},
toMB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.TB.toMB,
suffixes.mb,
);
},
toGB(opts = {}) {
return conv(
value,
opts.hr || false,
opts.round || false,
multipliers.TB.toGB,
suffixes.gb,
);
},
toTBHr(opts = {}) {
return conv(
value,
true,
opts.round || false,
multipliers.TB.toTBHr,
suffixes.tb,
);
},
};
},
};
}());
const testCases = [1, 10, 150, 1000, 74839.67346];
const HRSuffixes = Object.values(suffixes);
const roundDecimals = 2;
const precision = Number(`0.${'0'.repeat(roundDecimals)}5`);
const SCIENTIFIC_NOT_NUMBER_REGXP = /[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?/g;
const SUFFIX_REGXP = /[a-z]+$/i;
const CONVERSION_TO_REGXP = /(?<=to).*(?=hr+$)|(?<=to).*(?=hr+$)?/i;
for (const conversionFrom of (Object.keys(MemConv))) {
for (const tCase of testCases) {
const convFunc = MemConv[conversionFrom](tCase);
for (const [conversionToFn, f] of Object.entries(convFunc)) {
const conversionTo = (conversionToFn.match(CONVERSION_TO_REGXP) || [conversionToFn])[0];
const result = f();
const humanReadable = f({ hr: true });
const rounded = f({ round: roundDecimals });
const roundedAndHumanReadable = f({ hr: true, round: roundDecimals });
console.log({
value: tCase,
from: conversionFrom,
to: conversionTo,
result,
humanReadable,
rounded,
roundedAndHumanReadable,
});
}
}
}
TSVersion
test
import assert from 'assert';
function test() {
const testCases = [1, 10, 150, 1000, 74839.67346];
const HRSuffixes = Object.values(suffixes);
const roundDecimals = 2;
const precision = Number(`0.${'0'.repeat(roundDecimals)}5`);
const SCIENTIFIC_NOT_NUMBER_REGXP = /[-+]?[0-9]*.?[0-9]+([eE][-+]?[0-9]+)?/g;
const SUFFIX_REGXP = /[a-z]+$/i;
const CONVERSION_TO_REGXP = /(?<=to).*(?=hr+$)|(?<=to).*(?=hr+$)?/i;
for (const conversionFrom of (Object.keys(MemConv) as (keyof typeof MemConv)[])) {
for (const tCase of testCases) {
const convFunc = MemConv[conversionFrom](tCase);
for (const [conversionToFn, f] of Object.entries(convFunc)) {
const conversionTo = (conversionToFn.match(CONVERSION_TO_REGXP) || [conversionToFn])[0];
const expectedSuffix = suffixes[conversionTo.toLowerCase() as keyof typeof suffixes];
const multiplier = multipliers[conversionFrom][conversionToFn as keyof typeof multipliers[typeof conversionFrom]];
const expectedResult = tCase * multiplier > Number.MAX_SAFE_INTEGER
? Number.MAX_SAFE_INTEGER
: tCase * multiplier;
const result = f();
const humanReadable = f({ hr: true });
const rounded = f({ round: roundDecimals });
const roundedAndHumanReadable = f({ hr: true, round: roundDecimals });
const resHrNumber = Number((humanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
const resHrSuffix = (humanReadable.match(SUFFIX_REGXP) || [0])[0];
const resRoundHrNumber = Number((roundedAndHumanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
const resRoundHrSuffix = (roundedAndHumanReadable.match(SUFFIX_REGXP) || [0])[0];
if (/hr$/i.test(conversionToFn)) {
const resNumber = Number((humanReadable.match(SCIENTIFIC_NOT_NUMBER_REGXP) || [''])[0]);
const resSuffix = (humanReadable.match(SUFFIX_REGXP) || [0])[0];
assert(typeof result === 'string');
assert(typeof resSuffix === 'string');
assert(typeof resRoundHrNumber === 'number');
assert(typeof rounded === 'string');
assert(result === humanReadable);
assert(resSuffix === expectedSuffix);
assert(resNumber <= expectedResult + precision && resNumber >= expectedResult - precision);
} else {
assert(typeof result === 'number');
assert(result === resHrNumber);
assert(typeof rounded === 'number');
assert(result <= expectedResult + precision && result >= expectedResult - precision);
}
console.log({
value: tCase,
from: conversionFrom,
to: conversionToFn,
result,
humanReadable,
rounded,
roundedAndHumanReadable,
});
assert(typeof resHrSuffix === 'string');
assert(typeof resHrNumber === 'number');
assert(resHrSuffix === expectedSuffix);
assert(resHrSuffix === resRoundHrSuffix);
assert(HRSuffixes.includes(resHrSuffix));
}
}
}
}
test();
使用
// GB to GB humanReadable
console.log(MemConv.GB(11.1942).toGBHr()); // 11.1942GB;
// GB to MB
console.log(MemConv.GB(11.1942).toMB());// 11194.2;
// MB to MB humanReadable
console.log(MemConv.MB(11.1942).toGB({ hr: true }));// 0.011194200000000001GB;
// MB to MB humanReadable with rounding
console.log(MemConv.MB(11.1942).toGB({ hr: true, round: 3 }));// 0.011GB;