Pretty print ByteString to hex nibble-wise
Asked Answered
S

5

12

What's an idiomatic way of treating a bytestring nibblewise and pretty printing its hexadecimal (0-F) representation?

putStrLn . show . B.unpack
-- [1,126]

Which, upon further work

putStrLn . show . map (\x -> N.showIntAtBase 16 (DC.intToDigit) x "") . B.unpack
["1","7e"]

But what I really want is

["1","7","e"]

Or better yet

['1','7','e']

I could munge up ["1","7e"] but that string manipulation whereas I'd rather do numeric manipulation. Do I need to drop down to shifting and masking numeric values?

Slipway answered 7/12, 2011 at 8:39 Comment(0)
B
10

I'd like to elaborate on max taldykin's answer (that I have upvoted), which I think is over-complicated. There is no need for NoMonomorphismRestriction, printf or Data.List.

Here is my version:

import qualified Data.ByteString as B
import Numeric (showHex)

prettyPrint :: B.ByteString -> String
prettyPrint = concat . map (flip showHex "") . B.unpack

main :: IO ()
main = putStrLn . prettyPrint . B.pack $ [102, 117, 110]
Breakable answered 7/12, 2011 at 13:37 Comment(2)
You'll mess up the result here since showHex will not pad to 2.Prussian
@Prussian is right, this solution is incorrect. It cuts off any leading zeros. crockeea's answer at the bottom is correct.Discounter
A
17

You can now use Data.ByteString.Builder. To print a ByteString to its hex equivalent (with two hex digits per byte, in the right order, and efficiently), simply use:

toLazyByteString . byteStringHex

or

toLazyByteString . lazyByteStringHex

depending on which flavor of ByteString you have as input.

Agape answered 24/2, 2016 at 14:29 Comment(0)
B
10

I'd like to elaborate on max taldykin's answer (that I have upvoted), which I think is over-complicated. There is no need for NoMonomorphismRestriction, printf or Data.List.

Here is my version:

import qualified Data.ByteString as B
import Numeric (showHex)

prettyPrint :: B.ByteString -> String
prettyPrint = concat . map (flip showHex "") . B.unpack

main :: IO ()
main = putStrLn . prettyPrint . B.pack $ [102, 117, 110]
Breakable answered 7/12, 2011 at 13:37 Comment(2)
You'll mess up the result here since showHex will not pad to 2.Prussian
@Prussian is right, this solution is incorrect. It cuts off any leading zeros. crockeea's answer at the bottom is correct.Discounter
K
4

Somethig like this:

{-# LANGUAGE NoMonomorphismRestriction #-}

import qualified Data.ByteString as B
import Text.Printf
import Data.List
import Numeric

hex = foldr showHex "" . B.unpack
list = printf "[%s]" . concat . intersperse "," . map show

Test:

> let x = B.pack [102,117,110]
> list . hex $ x
"['6','6','7','5','6','e']"

Upd Oh, there is a stupid memory leak: of course you should replace foldr with foldl' (because laziness is not required here):

hex = foldl' (flip showHex) "" . B.unpack
Kisumu answered 7/12, 2011 at 9:22 Comment(1)
Of course the foldl' version prints the the bytes in reverse order, "little-endian" style.Agape
A
3

You have ["1","7e"] :: [String] concat ["1", "7e"] is "17e" :: String which is equal to [Char] and equal to ['1','7','e'] :: [Char].

Than you may split that String into pieces:

> Data.List.Split.splitEvery 1 . concat $ ["1", "7e"]
["1","7","e"]
it :: [[Char]]
Arissa answered 7/12, 2011 at 9:19 Comment(1)
Data.List.Split.splitEvery 1 == map (:[])Kisumu
H
0

If you just want a regular hex en/decoding of ByteStrings, you can use the memory package. They call the hex encoding Base16.

>>> let input = "Is 3 > 2?" :: ByteString
>>> let convertedTo base = convertToBase base input :: ByteString
>>> convertedTo Base16
"49732033203e20323f"

Full documentation: https://hackage.haskell.org/package/memory-0.18.0/docs/Data-ByteArray-Encoding.html#t:Base

Hymenopteran answered 22/11, 2022 at 21:11 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.