Parsing Various Units-Of-Measurement As Bytes In Node.js
After the long holiday weekend, I wanted to do a fun little exercise to help get the machinery firing again - you know, a little something to brush away the cobwebs. I thought it would be nice to parse strings as bytes. Now, I'm not talking about buffers; I mean taking a string like, "10Mb" (ie, ten megabytes) and returning the represented byte count (ie, 10485760).
To do this, I created a Node.js module that exposes two public methods - parse() and parseSafely(). The first method will attempt to parse the input, but will throw an error if the input cannot be parsed. The parseSafely() method will also attempt to parse the input; however, if the input cannot be parsed, parseSafely() will return a zero rather than throwing an error. I did this because I always found it extremely useful the way ColdFusion's val() method will return a zero if the value cannot be coerced to a number.
// Export the public API.
exports.parse = parse;
exports.parseSafely = parseSafely;
// I hold the multipliers that each unit of measurement needs in order to produce an
// equivalent byte count. For example, to convert from KB to B, you need to multiply
// by 1024.
var multipliers = {}
multipliers.B = 1;
multipliers.KB = ( multipliers.B * 1024 );
multipliers.MB = ( multipliers.KB * 1024 );
multipliers.GB = ( multipliers.MB * 1024 );
multipliers.TB = ( multipliers.GB * 1024 );
multipliers.PB = ( multipliers.TB * 1024 );
multipliers.EB = ( multipliers.PB * 1024 );
multipliers.ZB = ( multipliers.EB * 1024 );
// ---
// PUBLIC METHODS.
// ---
/**
* I parse the given input string as a number of bytes. Inputs are expected to be in the
* form of a numeric value followed by a unit of measurement (ex, KB, MB, GB). If the
* value cannot be parsed, I throw an error.
*
* @input I am the string value being parsed.
*/
function parse( input ) {
var value = parseValue( input );
var unit = parseUnit( input );
return( value * multipliers[ unit ] );
}
/**
* I parse the bytes from the given input; however, if the value cannot be parsed, I
* return zero rather than throwing an error.
*
* @input I am the string value being parsed.
*/
function parseSafely( input ) {
try {
return( parse( input ) );
} catch ( parsingError ) {
return( 0 );
}
}
// ---
// PRIVATE METHODS.
// ---
/**
* I parse the unit of measurement out of the given input. I allow for a unit of
* measurement that contains an optional "i" (ex, KB vs KiB). If a supported unit of
* measurement cannot be found, I throw an error.
*
* @input I am the string value being parsed.
*/
function parseUnit( input ) {
var matches = input.toUpperCase().match( /([KMGTPEZ]I?)?B$/ );
if ( ! matches ) {
throw( "Input does not contain a supported unit of measurement (ex, KB)." );
}
// Strip out any optional "i" that was used in the unit of measurement (ex, MiB).
var unit = matches[ 0 ].replace( /i/i, "" );
return( unit );
}
/**
* I parse the numeric value out of the given input. If the value cannot be parsed with
* a leading float value, I throw an error.
*
* @input I am the string value being parsed.
*/
function parseValue( input ) {
var value = parseFloat( input );
if ( isNaN( value ) ) {
throw( "Input does not contain a numeric value." );
}
return( value );
}
Once I had this Node.js module in place, I wanted to make sure that it was working properly. As luck would have it, Node.js comes with an "assert" module that can be used to write unit tests:
// Require our core node modules.
var assert = require( "assert" );
// Require our core application modules.
var byteParser = require( "./byte-parser" );
// Test parsing that should work.
assert.strictEqual( byteParser.parse( "1B" ), 1 );
assert.strictEqual( byteParser.parse( "10.5 B" ), 10.5 );
assert.strictEqual( byteParser.parse( "705 KB" ), 721920 );
assert.strictEqual( byteParser.parse( "11.7Mb" ), 12268339.2 );
assert.strictEqual( byteParser.parse( "5.5 MiB" ), 5767168 );
assert.strictEqual( byteParser.parse( "8Gb" ), 8589934592 );
assert.strictEqual( byteParser.parse( "2.745 TB" ), 3018159418245.12 );
// Test parsing that should fail safely.
assert.strictEqual( byteParser.parseSafely( "2.745 Foo" ), 0 );
assert.strictEqual( byteParser.parseSafely( "Meh!" ), 0 );
// Test parsing that should fail loudly.
assert.throws(
function () {
byteParser.parse( "3.5 Cows" );
}
);
console.log( "Sweet chickens, all tests passed!" );
As you can see, I'm testing the parser against some known good values. And, when we run this Node.js code, we get the following terminal output:
Sweet chickens, all tests passed!
Anyway, this was just a fun little exercise to get my ready to dive back into a week of serious problem solving and grueling work. Once more into the fray.... Into the last good fight I'll ever know.
Want to use code from this post? Check out the license.
Reader Comments