2016-06-01 7 views
8

Próbujesz napisać moduł, który zwraca zewnętrzny adres IP mojego komputera. Funkcja, a następnie zastosowanie soczewki w celu uzyskania responseBody, typ I kończy się z Data.ByteString.Lazy.Internal.ByteString. Ponieważ chcę odfiltrować końcowy "\ n" wynikowego ciała, chcę użyć tego później do wyrażeń regularnych. Problem: Ten pozornie bardzo specyficzny typ ByteString nie został zaakceptowany przez bibliotekę regex i nie znalazłem możliwości przekonwertowania go na String.Data.ByteString.Lazy.Internal.ByteString na ciąg?

Oto moja słaba próba do tej pory (nie kompilacja).

{-# 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) 

Więc moje pytanie brzmi oczywiście: Jak przekonwertować śmieszne specjalną ByteString do String? Wyjaśnienie również, w jaki sposób sam mogę podejść do tego problemu. Próbowałem użyć unpack i toString, ale nie mam pojęcia, co zaimportować, aby uzyskać te funkcje, jeśli istnieją.

Będąc bardzo sporadycznym użytkownikiem haskell, zastanawiam się też, czy ktoś mógłby mi pokazać idiomatyczny sposób określania takiej funkcji. Wersja, którą tu pokazuję, w końcu nie uwzględnia możliwych błędów/wyjątków w czasie wykonywania.

+2

myślę, że to przesada, aby użyć wyrażenia regularnego w tym przypadku i przejść do wersji, która nadal wykorzystuje 'ByteString' można zaimplementować coś takiego jak' trim' łatwo przycinać i '' dropWhile' Data.Char.isSpace' i 'reverse'. – epsilonhalbe

+0

, aby dowiedzieć się, gdzie można znaleźć funkcje - użyj goo ... uhm no - [hoogle] (https://www.haskell.org/hoogle/?hoogle = isspace) – epsilonhalbe

+0

@epsilonhalbe Tak więc w moim przypadku, biorąc pod uwagę, że już zaimportowałem '' Data.ByteString.Lazy'', będzie to '' let body = Char8.unpack (r ^. responseBody) ''? U mnie to daje: extip.hs: 6: 1: error: Nie można załadować interfejsu dla 'Data.BytesString.Lazy ' Być może chodziło Ci o Data.ByteString.Lazy (z bytestring-0.10.8.1) Data.ByteString . Obiektyw (z obiektywu-4.14) Data.ByteString.Char8 (od bytestring-0.10.8.1) Użyj opcji -v, aby wyświetlić listę szukanych plików. Błąd, załadowane moduły: brak. – BitTickler

Odpowiedz

1

Po prostu nie rozumiem, dlaczego nalegasz na używanie String s, gdy masz już pod ręką ByteString, która jest szybszą/bardziej wydajną implementacją. Importowanie regex nie daje prawie żadnych korzyści - do analizowania adresu IP użyłbym attoparsec, który świetnie współpracuje z ByteString s.

Oto wersja, która nie używa wyrażenia regularnego, ale zwraca ciąg znaków - notatka, której nie skompilowałem, ponieważ nie mam ustawienia haskell, gdzie jestem teraz.

{-# 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) 
+0

To, czego chcę, to zwrócić typ kanoniczny, który nie generuje dodatkowej pracy (takie jak to pytanie SO tutaj). Gdyby Wreq zwrócił String, nie miałbym kłopotów, z jakimi mam do czynienia. Jeśli język nie ma typu kanonicznego, gdy używane są biblioteki, powstają problemy z konwersją. Zadzwoń do mnie staroświecki ... ale wolę trzymać się jednego typu kanonicznego na granicy interfejsu. – BitTickler

+0

extip.hs: 14: 41: error: * Nie można dopasować typ 'GHC.Word.Word8' z' char” oczekiwano typu: GHC.Word.Word8 -> Bool Rzeczywisty typ: Char -> Bool * W pierwszym argumencie 'B.dropWhile ', a mianowicie' isSpace' W pierwszym argumencie '(.) ', A mianowicie' (B.dropWhile isSpace)' W drugim argumencie '(.) ', A mianowicie '(B.dropWhile isSpace). Char8.reverse. (B.dropWhile isSpace) " ... – BitTickler

+0

Myślę, że to naprawiłem - nie mam pod ręką żadnego ghc (tak jak już powiedziałem). – epsilonhalbe

8

Krótka odpowiedź: Użyj unpack z Data.ByteString.Lazy.Char8

Dłuższa odpowiedź:

W ogóle, jeśli chcesz przekonwertować ByteString (dowolnej odmiany) do typu String lub tekst musisz określić kodowanie - np UTF-8 lub Latin1 itd.

Podczas pobierania strony HTML kodowanie, które ma być użyte, może pojawić się w nagłówku Content-Content lub w treści odpowiedzi jako znacznik <meta ...>.

Alternatywnie możesz po prostu zgadnąć, jakie jest kodowanie ciała.

W twoim przypadku zakładam, że wchodzisz na stronę taką jak http://whatsmyip.org i musisz tylko sparsować swój adres IP. Zatem bez sprawdzania nagłówków lub przeglądania kodu HTML bezpiecznym kodowaniem będzie użycie Latin1.

Aby przekonwertować ByteStrings do tekstu poprzez kodowanie, przyjrzeć się funkcji w Data.Text.Encoding

na przykład funkcji decodeLatin1.

+0

Podane: '' let b1 = r ^. responseBody'', '' decodeLatin1 b1'' wydajność: : 119: 14: error: * Nie można dopasować oczekiwanego typu 'Data.ByteString.ByteString ' z rzeczywistym typem' Data.ByteString.Lazy.ByteString' NB : 'Data.ByteString.Lazy.ByteString ' jest zdefiniowane w' Data.ByteString.Lazy.Internal' 'Data.ByteString.ByteString ' jest zdefiniowane w' Data.ByteString.Internal' * w pierwszym argumentem ' decodeLatin1 ', a mianowicie 'b1' W wyrażeniu: decodeLatin1 b1 W równaniu" it ": it = decodeLatin1 b1 – BitTickler

+0

Z dokumentów nie jest łatwo zobaczyć, ale dekodeLatin1 wymaga _strict_ByteString. Użyj 'toStrict', aby najpierw przekonwertować leniwy ciąg znaków. – ErikR