Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Data.ByteString.Lazy.Internal.ByteString to string?

Tags:

haskell

Trying to write a module which returns the external IP address of my computer. Using Network.Wreq get function, then applying a lense to obtain responseBody, the type I end up with is Data.ByteString.Lazy.Internal.ByteString. As I want to filter out the trailing "\n" of the result body, I want to use this for a regular expression subsequently. Problem: That seemingly very specific ByteString type is not accepted by regex library and I found no way to convert it to a String.

Here is my feeble attempt so far (not compiling).

{-# LANGUAGE OverloadedStrings #-}

module ExtIp (getExtIp) where
import Network.Wreq
import Control.Lens
import Data.BytesString.Lazy
import Text.Regex.Posix

getExtIp :: IO String
getExtIp = do
    r <- get "http://myexternalip.com/raw"
    let body = r ^. responseBody
    let addr = body =~ "[^\n]*\n"
    return (addr)

So my question is obviously: How to convert that funny special ByteString to a String? Explaining how I can approach such a problem myself is also appreciated. I tried to use unpack and toString but have no idea what to import to get those functions if they exist.

Being a very sporadic haskell user, I also wonder if someone could show me the idiomatic haskell way of defining such a function. The version I show here does not account for possible runtime errors/exceptions, after all.

like image 396
BitTickler Avatar asked Jun 01 '16 14:06

BitTickler


2 Answers

Short answer: Use unpack from Data.ByteString.Lazy.Char8

Longer answer:

In general when you want to convert a ByteString (of any variety) to a String or Text you have to specify an encoding - e.g. UTF-8 or Latin1, etc.

When retrieving an HTML page the encoding you are suppose to use may appear in the Content-type header or in the response body itself as a <meta ...> tag.

Alternatively you can just guess at what the encoding of the body is.

In your case I presume you are accessing a site like http://whatsmyip.org and you only need to parse out your IP address. So without examining the headers or looking through the HTML, a safe encoding to use would be Latin1.

To convert ByteStrings to Text via an encoding, have a look at the functions in Data.Text.Encoding

For instance, the decodeLatin1 function.

like image 135
ErikR Avatar answered Nov 10 '22 07:11

ErikR


I simply do not understand why you insist on using Strings, when you have already a ByteString at hand that is the faster/more efficient implementation. Importing regex gives you almost no benefit - for parsing an ip-address I would use attoparsec which works great with ByteStrings.

Here is a version that does not use regex but returns a String - note I did not compile it for I have no haskell setup where I am right now.

{-# LANGUAGE OverloadedStrings #-}

module ExtIp (getExtIp) where
import Network.Wreq
import Control.Lens
import Data.ByteString.Lazy.Char8 as Char8
import Data.Char (isSpace)

getExtIp :: IO String
getExtIp = do
    r <- get "http://myexternalip.com/raw"
    return $ Char8.unpack $ trim (r ^. responseBody)
  where trim = Char8.reverse . (Char8.dropWhile isSpace) . Char8.reverse . (Char8.dropWhile isSpace)
like image 37
epsilonhalbe Avatar answered Nov 10 '22 07:11

epsilonhalbe