more OO way to do hex string to bytes conversion

H. S. Teoh hsteoh at quickfur.ath.cx
Tue Feb 6 18:46:54 UTC 2018


On Tue, Feb 06, 2018 at 06:33:02PM +0000, Ralph Doncaster via Digitalmars-d-learn wrote:
> I've been reading std.conv and std.range, trying to figure out a
> high-level way of converting a hex string to bytes.  The only way I've
> been able to do it is through pointer access:
> 
> import std.stdio;
> import std.string;
> import std.conv;
> 
> void main()
> {
>     immutable char* hex = "deadbeef".toStringz;
>     for (auto i=0; hex[i]; i += 2)
>         writeln(to!byte(hex[i]));
> }
> 
> 
> While it works, I'm wondering if there is a more object-oriented way
> of doing it in D.

OO is outdated.  D uses the range-based idiom with UFCS for chaining
operations in a way that doesn't require you to write loops yourself.
For example:

	import std.array;
	import std.algorithm;
	import std.conv;
	import std.range;

	// No need to use .toStringz unless you're interfacing with C
	auto hex = "deadbeef";	// let compiler infer the type for you

	auto bytes = hex.chunks(2)	// lazily iterate over `hex` by digit pairs
	   .map!(s => s.to!ubyte(16))	// convert each pair to a ubyte
	   .array;			// make an array out of it

	// Do whatever you wish with the ubyte[] array.
	writefln("%(%02X %)", bytes);

If you want a reusable way to convert a hex string to bytes, you could
do something like this:

	import std.array;
	import std.algorithm;
	import std.conv;
	import std.range;

	ubyte[] hexToBytes(string hex)
	{
		return hex.chunks(2)
		          .map!(s => s.to!ubyte(16))
			  .array;
	}

Of course, this eagerly constructs an array to store the result, which
allocates, and also requires the hex string to be fully constructed
first.  You can make this code lazy by turning it into a range
algorithm, then you can actually generate the hex digits lazily from
somewhere else, and process the output bytes as they are generated, no
allocation necessary:

	/* Run this example by putting this in a file called 'test.d'
	 * and invoking `dmd -unittest -main -run test.d`
	 */
	import std.array;
	import std.algorithm;
	import std.conv;
	import std.format;
	import std.range;
	import std.stdio;

	auto hexToBytes(R)(R hex)
		if (isInputRange!R && is(ElementType!R : dchar))
	{
		return hex.chunks(2)
		          .map!(s => s.to!ubyte(16));
	}

	unittest
	{
		// Infinite stream of hex digits
		auto digits = "0123456789abcdef".cycle;

		digits.take(100)	// take the first 100 digits
		      .hexToBytes	// turn them into bytes
		      .map!(b => format("%02X", b)) // print in uppercase
		      .joiner(" ")	// nicely delimit bytes with spaces 
		      .chain("\n")	// end with a nice newline
		      .copy(stdout.lockingTextWriter);
		      			// write output directly to stdout
	}


T

-- 
Designer clothes: how to cover less by paying more.


More information about the Digitalmars-d-learn mailing list