2016-12-20 5 views
2

Ich lese eine CSV-Datei mit Pipes-CSV-Bibliothek. Ich möchte die erste Zeile lesen und den Rest später lesen. Leider nach Pipes.Prelude.head Funktion zurück. Das Rohr wird irgendwie geschlossen. Gibt es eine Möglichkeit, den Kopf des CSV zuerst zu lesen und den Rest später zu lesen.Erste Zeile aus einer CSV-Datei mit Pipes-Csv lesen

import qualified Data.Vector as V 
import Pipes 
import qualified Pipes.Prelude as P 
import qualified System.IO as IO 
import qualified Pipes.ByteString as PB 
import qualified Data.Text as Text 
import qualified Pipes.Csv as PCsv 
import Control.Monad (forever) 

showPipe :: Proxy() (Either String (V.Vector Text.Text))() String IO b 
showPipe = forever $ do 
    x::(Either String (V.Vector Text.Text)) <- await 
    yield $ show x 


main :: IO() 
main = do 
    IO.withFile "./test.csv" 
       IO.ReadMode 
       (\handle -> do 
        let producer = (PCsv.decode PCsv.NoHeader (PB.fromHandle handle)) 
        headers <- P.head producer 
        putStrLn "Header" 
        putStrLn $ show headers 
        putStrLn $ "Rows" 
        runEffect (producer>-> 
           (showPipe) >-> 
           P.stdoutLn) 
       ) 

Wenn wir den Header nicht lesen zuerst, können wir ganze csv ohne Probleme lesen:

main :: IO() 
main = do 
    IO.withFile "./test.csv" 
       IO.ReadMode 
       (\handle -> do 
        let producer = (PCsv.decode PCsv.NoHeader (PB.fromHandle handle)) 
        putStrLn $ "Rows" 
        runEffect (producer>-> 
           (showPipe) >-> 
           P.stdoutLn) 
       ) 

Antwort

1

Pipes.Csv hat Material für Header Handling, aber ich denke, dass diese Frage wirklich für eine sucht anspruchsvollere Verwendung von Pipes.await oder auch Pipes.next. next Erstens:

>>> :t Pipes.next 
Pipes.next :: Monad m => Producer a m r -> m (Either r (a, Producer a m r)) 

next ist der grundlegende Weg, um einen Hersteller von Inspektion. Es ist eine Art Mustervergleich in einer Liste. Bei einer Liste sind die beiden Möglichkeiten [] und x:xs - hier sind es Left() und Right (headers, rows). Das letzte Paar ist, was Sie suchen. Natürlich eine Aktion (hier in IO) benötigt man die Hände auf sie zu erhalten:

main :: IO() 
main = do 
    handle <- IO.openFile "./test.csv" IO.ReadMode 
    let producer :: Producer (V.Vector Text.Text) IO() 
     producer = PCsv.decode PCsv.NoHeader (PB.fromHandle handle) >-> P.concat 
    e <- next producer 
    case e of 
    Left() -> putStrLn "No lines!" 
    Right (headers, rows) -> do 
     putStrLn "Header" 
     print headers 
     putStrLn $ "Rows" 
     runEffect (rows >-> P.print) 
    IO.hClose handle 

Da die Either Werte Ablenkung hier sind, ich Left Werte beseitigen - die Linien, die nicht analysieren - mit P.concat

next wirkt nicht innerhalb einer Pipeline, sondern direkt auf der Producer, die es als eine Art "effektive Liste" mit einem endgültigen Rückgabewert am Ende behandelt. Der besondere Effekt, den wir oben erhalten haben, kann natürlich mit await erreicht werden, der innerhalb einer Pipeline wirkt. Ich kann es verwenden, um den ersten Punkt abfangen, die zusammen in einer Pipeline kommt, einige IO basiert darauf, und dann leiten die übrigen Elemente:

main :: IO() 
main = do 
    handle <- IO.openFile "./grades.csv" IO.ReadMode 
    let producer :: Producer (V.Vector Text.Text) IO() 
     producer = PCsv.decode PCsv.NoHeader (PB.fromHandle handle) >-> P.concat 
     handleHeader :: Pipe (V.Vector Text.Text) (V.Vector Text.Text) IO() 
     handleHeader = do 
     headers <- await -- intercept first value 
     liftIO $ do  -- use it for IO 
      putStrLn "Header" 
      print headers 
      putStrLn $ "Rows" 
     cat    -- pass along all later values 
    runEffect (producer >-> handleHeader >-> P.print) 
    IO.hClose handle 

Der Unterschied ist nur, dass, wenn producer leer ist, ich gewann‘ t kann dies erklären, wie ich mit No lines! im vorherigen Programm.

Hinweis durch die Art und Weise, dass showPipe als P.map show definiert werden kann, oder einfach als P.show (aber mit dem Fachtyp, den Sie hinzufügen.)

+0

P.next war Präfekt Lösung für meinen Fall. Danke auch für die Anzeige der Kartenversion von showPipe. – yilmazhuseyin

+0

Auch die Entweder mit P.concat zu entfernen war wirklich ein schöner Trick. – yilmazhuseyin

Verwandte Themen