Reputation: 13562
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:
Example input: [|0x24uy; 0xA1uy; 0x00uy; 0x1Cuy|] should produce string "24A1001C"
Upvotes: 7
Views: 6270
Reputation: 165
BitConverter
has better performance
BitConverter.ToString(str).Replace("-", "")
Method | Mean | Error | StdDev |
---|---|---|---|
BitConverter | 398.4 ns | 3.33 ns | 2.78 ns |
concat | 1,465.9 ns | 13.52 ns | 10.56 ns |
fold | 1,497.6 ns | 10.23 ns | 8.54 ns |
StringBuilder | 1,805.8 ns | 14.92 ns | 13.23 ns |
Upvotes: 0
Reputation: 31799
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"
Upvotes: 5
Reputation: 21
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 (+)
Upvotes: 2
Reputation: 9915
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.
Upvotes: 3
Reputation: 47934
Looks fine to me. Just to point out another, in my opinion, very helpful function in the Printf module, have a look at ksprintf
. It passes the result of a formated string into a function of your choice (in this case, the identity function).
val ksprintf : (string -> 'd) -> StringFormat<'a,'d> -> 'a
sprintf, but call the given 'final' function to generate the result.
Upvotes: 1
Reputation: 754585
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.
Upvotes: 11
Reputation: 1062610
To be honest, that doesn't look terrible (although I also have very little F# experience). Does F# offer an easy way to iterate (foreach)? If this was C#, I might use something like (where raw
is a byte[]
argument):
StringBuilder sb = new StringBuilder();
foreach (byte b in raw) {
sb.Append(b.ToString("x2"));
}
return sb.ToString()
I wonder how that translates to F#...
Upvotes: -1