Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

f# byte[] -> hex -> string conversion

Tags:

f#

I have byte array as input. I would like to convert that array to string that contains hexadecimal representation of array values. This is F# code:

let ByteToHex bytes = 
    bytes 
    |> Array.map (fun (x : byte) -> String.Format("{0:X2}", x))

let ConcatArray stringArray = String.Join(null, (ByteToHex  stringArray))

This produces result I need, but I would like to make it more compact so that I have only one function. I could not find function that would concat string representation of each byte at the end of ByteToHex.
I tried Array.concat, concat_map, I tried with lists, but the best I could get is array or list of strings.

Questions:

  1. What would be simplest, most elegant way to do this?
  2. Is there string formatting construct in F# so that I can replace String.Format from System assembly?

Example input: [|0x24uy; 0xA1uy; 0x00uy; 0x1Cuy|] should produce string "24A1001C"

like image 638
zendar Avatar asked Dec 21 '08 13:12

zendar


4 Answers

There is nothing inherently wrong with your example. If you'd like to get it down to a single expression then use the String.contcat method.

let ByteToHex bytes = 
    bytes 
    |> Array.map (fun (x : byte) -> System.String.Format("{0:X2}", x))
    |> String.concat System.String.Empty

Under the hood, String.concat will just call into String.Join. Your code may have to be altered slighly though because based on your sample you import System. This may create a name resolution conflict between F# String and System.String.

like image 167
JaredPar Avatar answered Oct 07 '22 22:10

JaredPar


If you want to transform and accumulate in one step, fold is your answer. sprintf is the F# string format function.

let ByteToHex (bytes:byte[]) =
    bytes |> Array.fold (fun state x-> state + sprintf "%02X" x) ""

This can also be done with a StringBuilder

open System.Text

let ByteToHex (bytes:byte[]) =
        (StringBuilder(), bytes)
        ||> Array.fold (fun state -> sprintf "%02X" >> state.Append)  
        |> string

produces:

[|0x24uy; 0xA1uy; 0x00uy; 0x1Cuy|] |> ByteToHex;;
val it : string = "24A1001C"
like image 41
jbtule Avatar answered Oct 07 '22 23:10

jbtule


Here's another answer:

let hashFormat (h : byte[]) =
  let sb = StringBuilder(h.Length * 2)
  let rec hashFormat' = function
    | _ as currIndex when currIndex = h.Length -> sb.ToString()
    | _ as currIndex ->
      sb.AppendFormat("{0:X2}", h.[currIndex]) |> ignore
      hashFormat' (currIndex + 1)
  hashFormat' 0

The upside of this one is that it's tail-recursive and that it pre-allocates the exact amount of space in the string builder as will be required to convert the byte array to a hex-string.

For context, I have it in this module:

module EncodingUtils

open System
open System.Text
open System.Security.Cryptography
open Newtonsoft.Json

let private hmacmd5 = new HMACMD5()
let private encoding = System.Text.Encoding.UTF8
let private enc (str : string) = encoding.GetBytes str
let private json o = JsonConvert.SerializeObject o
let md5 a = a |> (json >> enc >> hmacmd5.ComputeHash >> hashFormat)

Meaning I can pass md5 any object and get back a JSON hash of it.

like image 35
Henrik Avatar answered Oct 07 '22 22:10

Henrik


Here's another. I'm learning F#, so feel free to correct me with more idiomatic ways of doing this:

let bytesToHexString (bytes : byte[]) : string =
    bytes
    |> Seq.map (fun c -> c.ToString("X2"))
    |> Seq.reduce (+)
like image 24
Tom Avatar answered Oct 07 '22 21:10

Tom