8 Stimmen

Haskell: Schreiben von Textdateien und Parsen in das ursprüngliche Format

Ich habe eine Liste von Tupeln des Formats [(String,String)] und benötige eine Funktion, um den Inhalt der Liste in eine Textdatei zu schreiben, dann eine andere Funktion, um diese Textdatei als dieselbe Liste von Tupeln einzulesen. Hier ist, was ich für die Speicherfunktion habe:

save :: Table -> IO()
save [] = writeFile "database.txt" ""
save zs = do { writeFile "database.txt" "" ; sequence_ [appendFile "database.txt" ("("++a++","++b++")\n") | (a,b) <- zs] }

Wäre das ein gutes Format für die Textdatei? Wie könnte ich dann diese Textdatei einlesen und wieder in die Liste der Tupel umwandeln?

12voto

ephemient Punkte 189038

Definiert in Prelude ,

type ShowS = String -> String
class Show a where
    showsPrec :: Int -> a -> ShowS
    show :: a -> String
    showList :: [a] -> ShowS

type ReadS a = String -> [(a, String)]
class Read a where
    readsPrec :: Int -> ReadS a
    readList :: ReadS [a]
read :: (Read a) => String -> a

Kurz gesagt, dies sind die Standard-"Serialisierungs"-Methoden in Haskell. show :: (Show a) => a -> String kann alles verwandeln, was eine Instanz von Show in eine Zeichenkette und read :: (Read a) => String -> a kann eine Zeichenkette in alles verwandeln, was eine Instanz von Read (oder eine Ausnahme auslösen).

Die meisten der eingebauten Typen und Datenstrukturen in der Standardbibliothek haben Show y Read Instanzen definiert; wenn Sie Teile daraus zusammensetzen, hat Ihr Typ auch Show y Read Instanzen definiert.

type Table = [(String, String)]

load :: (Read a) => FilePath -> IO a
load f = do s <- readFile f
            return (read s)

save :: (Show a) => a -> FilePath -> IO ()
save x f = writeFile f (show x)

Si Table ein Datentyp sind, müssen Sie nach den Instanzen fragen, aber Sie können verlangen, dass der Compiler sie automatisch für Sie ableitet.

data Table = Table [(String, String)]
    deriving (Read, Show)

Manchmal ist das nicht möglich und Sie müssen Ihre eigenen Instanzen definieren.

instance Show Table where
    showsPrec p x = ...
instance Read Table where
    readsPrec p x = ...

Aber das sollte nicht üblich sein.

5voto

Tom Lokhorst Punkte 13200

El show / read wird gut funktionieren, ich verwende sie auch, aber nur für kleine Werte. Bei größeren, komplexeren Werten read wird sehr langsam sein.

Dieses erfundene Beispiel zeigt die schlechte Leistung von read :

data RevList a = (RevList a) :< a | Nil
  deriving (Show, Read)

ghci> read "(((((((((((((((Nil)))))))))))))))" :: RevList Int

Auch, read nicht in der Lage sein, einige gültige Haskell-Ausdrücke zu lesen, insbesondere solche, die Infix-Konstruktoren verwenden (wie die :< in meinem Beispiel). Der Grund dafür ist, dass read ist sich der Unveränderlichkeit der Betreiber nicht bewusst. Dies ist auch der Grund, warum show $ Nil :< 1 :< 2 :< 3 wird eine Menge scheinbar überflüssiger Klammern erzeugen.

Wenn Sie die Serialisierung für größere Werte haben wollen, würde ich vorschlagen, eine andere Bibliothek zu verwenden, wie Daten.Binär . Dies wird etwas komplexer sein als eine einfache show vor allem wegen des Mangels an deriving Binary . Es gibt jedoch verschiedene generische Programmierlösungen, die Ihnen deriving -ähnlichen Surrogaten.

Schlussfolgerung: Ich würde sagen, verwenden Sie die show / read Lösung, bis Sie an ihre Grenzen stoßen (wahrscheinlich, wenn Sie anfangen, tatsächliche Anwendungen zu entwickeln), dann sollten Sie sich nach etwas Skalierbarerem (aber auch Komplexerem) wie Data.Binary umsehen.


Nebenbemerkung: Für diejenigen, die sich für Parser und fortgeschrittenere Haskell-Sachen interessieren: Die Beispiele, die ich gegeben habe, stammen aus dem Papier: Haskel Do You Read Me? auf eine Alternative, schnell read -ähnliche Funktion.

3voto

sth Punkte 210180

Mit Ihrer derzeitigen Funktion haben Sie ein Problem, wenn die Zeichenketten in der Liste "," oder ")" enthalten, denn dann ist es unmöglich herauszufinden, wo eine Zeichenkette endet, wenn Sie versuchen, die Daten erneut einzulesen. Sie müssten diese Zeichen auf irgendeine Weise ausschließen, wenn sie in einer Zeichenkette erscheinen.

Es ist viel einfacher zu benutzen show y read um Ihre Daten in Zeichenketten umzuwandeln, und damals, um es selbst zu tun:

save :: Table -> IO ()
save zs = writeFile "database.txt" (show zs)

show umgeht Sonderzeichen und stellt sicher, dass die Daten in einem Format vorliegen, das von read . Um die Daten zu laden, lesen Sie die Datei in einen String und übergeben diesen an read um sie in die gewünschte Datenstruktur zu konvertieren.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X