2017-03-08 11 views
4

最終的にファイル全体をメモリにロードせずに、一度に1行ずつファイルを取得する簡単な方法があるのだろうかと思いました。私はattoparsecパーサで回線を折りたたみたいと思います。私はhGetLineData.Text.Lazy.IOを使ってみました。それは私の記憶を吹き飛ばします。私は後で読むと、最終的にファイル全体が読み込まれます。大きなファイルの大きな行をバッファリングせずに読み取る

s <- Pipes.sum $ 
    folds (\i _ -> (i+1)) 0 id (view Text.lines (Text.fromHandle handle)) 
print s 

だけで行数をカウントし、それはいくつかのグラグラもの「hGetChunkやっているように見えるために:無効な引数を(無効なバイトシーケンスを

私もfoldsview linesパイプ-テキストを使用してみました)」と表示され、wc -lが1分かかるところで11分かかります。 パイプのテキストには巨大な線に問題があると聞きましたか? (各行は約1GBです)

私は本当に提案には開放されており、初心者以外の多くの検索を見つけることができませんreadLineハウツー。

ありがとうございます!

+3

ライブラリのアドバイスとして、Pipes.Bytestring + decodingではなく入力用にPipes.Text.IOを使用しています。エラーメッセージは、各チャンクのシステム解読を判断しているテキストライブラリからのメッセージです。私はそれが、エンコーディングが何であると思ってもチャンクを理解することができないと言っていると仮定します。 – Michael

答えて

6

次のコードは、導管を使用し、意志:

  • UTF8デコード標準入力
  • があればライン毎
  • 利用可能なより多くのデータがあるようlineCコンビネータを実行し、単にyield値を1を入力して、行全体を一度に読み取ることなく、行内容を破棄します。
  • 1を合計して

yield 1コードを個々の行で処​​理するコードに置き換えることができます。

#!/usr/bin/env stack 
-- stack --resolver lts-8.4 --install-ghc runghc --package conduit-combinators 
import Conduit 

main :: IO() 
main = (runConduit 
    $ stdinC 
    .| decodeUtf8C 
    .| peekForeverE (lineC (yield (1 :: Int))) 
    .| sumC) >>= print 
+1

うわー、素晴らしい! 1m23s対1m5s "wc -l"、ありがとう! –

+0

時間を報告してくれてありがとう、よろしくお願いします。 –

+0

これが最も良い場所であるかどうかは分かりませんが、3 intの一定サイズのモノイド上の解析された行の上でsumCの代わりにfoldCを実行すると、再びすべてのメモリを吹き飛ばすようです。私は何かを残していますか?私もfoldlCを試しました。 –

3

これは、デコードされたテキストストリームそれはコンマと数字のちょうど長い行だった私は、生成されたファイルのためのwc -lよりも約14%長くかかる

{-#LANGUAGE BangPatterns #-} 
import Pipes 
import qualified Pipes.Prelude as P 
import qualified Pipes.ByteString as PB 
import qualified Pipes.Text.Encoding as PT 
import qualified Control.Foldl as L 
import qualified Control.Foldl.Text as LT 
main = do 
    n <- L.purely P.fold (LT.count '\n') $ void $ PT.decodeUtf8 PB.stdin 
    print n 

倍以上のように、おそらく最も簡単です。ドキュメントが言うようにIOはPipes.ByteStringで正しく実行されなければなりません。残りはさまざまな種類の便利さです。

attoparsecパーサは、view linesと区別される各行に割り当てることができますが、attoparsecパーサーはテキスト全体を累積することができ、1ギガバイトのテキストチャンクよりも優れているとは限りません。各行に繰り返し数字(例:単語で区切られた数字)がある場合は、Pipes.Attoparsec.parsedを使用してそれらをストリーミングできます。

+0

ありがとう!私はチャンスを得るときにもこれを試してみましょう –

関連する問題