2016-12-20 118 views
2

我正在使用pipes-csv库读取csv文件。我想阅读第一行,稍后阅读其他内容。不幸的是在Pipes.Prelude.head函数返回之后。莫名其妙地关闭了管道。有没有办法先读取csv的头部,然后再阅读其余部分。使用管道从csv文件读取第一行-csv

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

如果我们不先读头,我们可以看到整个CSV没有任何问题:

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

回答

1

Pipes.Csv具有处理头的材料,但我觉得这个问题真的找更复杂的使用Pipes.await或其他Pipes.next。首先next

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

next是检查生产者的基本途径。它有点像列表中的模式匹配。列表中有两种可能性:[]x:xs - 这里是Left()Right (headers, rows)。后者是你正在寻找的。需要当然一个动作(在IO这里)获取一个人的手就可以了:

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 

由于Either值在这里分心,我消除Left值 - 不解析线 - 与P.concat

next不在流水线内部,而是直接在Producer上,它被视为一种“有效列表”,并在最后有一个最终返回值。我们上面得到的特殊效果当然可以通过await来实现,它在流水线内部起作用。我可以用它来拦截在管道走来的第一个项目,做一些IO此基础上,然后将余下的元素:

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 

所不同的只是,如果producer是空的,我赢了”无法宣布这一点,就像我在前面的程序中使用No lines!一样。

的说明方式showPipe可以定义为P.map show,或简称为P.show(但与特殊类型的添加。)

+0

P.next是我的情况知府解决方案。也感谢您展示showPipe的地图版本。 – yilmazhuseyin

+0

同时用P.concat删除Either是非常好的技巧。 – yilmazhuseyin