Attoparsec :Sauter les termes entre crochets ?
-
13-12-2019 - |
Question
J'essaie de créer de gros fichiers TSV avec JSON dans la 5ème colonne adaptés à l'importation dans mongoDB.En particulier, je souhaite modifier les champs clés de niveau supérieur et uniquement de niveau supérieur en _id.Voici ce que j'ai jusqu'à présent, cela semble fonctionner mais c'est lent :
{-# LANGUAGE OverloadedStrings #-}
import System.Environment (getArgs)
import Data.Conduit.Binary (sourceFile, sinkFile)
import Data.Conduit
import qualified Data.Conduit.Text as CT
import qualified Data.Conduit.List as CL
import qualified Data.Text as T
import Data.Monoid ((<>))
import Data.Attoparsec.Text as APT
import Control.Applicative
main = do
(inputFile : outputFile : _) <- getArgs
runResourceT $ sourceFile inputFile
$= CT.decode CT.utf8 $= CT.lines $= CL.map jsonify
$= CT.encode CT.utf8 $$ sinkFile outputFile
jsonify :: T.Text -> T.Text
jsonify = go . T.splitOn "\t"
where
go (_ : _ : _ : _ : content : _) = case parseOnly keyTo_id content of
Right res -> res <> "\n"
_ -> ""
go _ = ""
keyTo_id :: Parser T.Text
keyTo_id = skipWhile(/='{') >> T.snoc <$>
(T.cons <$> (char '{')
<*> (T.concat <$> many1 ( bracket
<|> (string "\"key\":" >> return "\"_id\":")
<|> APT.takeWhile1(\x -> x /= '{' && x /= '}' && x/= '"')
<|> T.singleton <$> satisfy (/= '}')
)))
<*> char '}'
bracket :: Parser T.Text
bracket = T.cons <$> char '{'
<*> scan 1 test
where
test :: Int -> Char -> Maybe Int
test 0 _ = Nothing
test i '}'= Just (i-1)
test i '{' = Just (i+1)
test i _ = Just i
Selon le profileur, 58,7% du temps est passé en bracket, 19,6% en keyTo_id, 17,1% en main.
Il existe sûrement un meilleur moyen de renvoyer les termes entre parenthèses inchangés si les parenthèses correspondent ?
J'ai brièvement regardé attoparsec-conduit, mais je n'ai aucune idée de comment utiliser cette bibliothèque et je ne peux même pas dire si c'est le genre de chose pour laquelle elle peut être utilisée.
MODIFIER:Mise à jour du code.Les données proviennent d'openlibrary.org, par ex.g. http://openlibrary.org/data/ol_dump_authors_latest.txt.gz
La solution
Utilisez le scan
fonction.Il vous permet de parcourir une chaîne en conservant un état.Dans votre cas, l'état sera un nombre - la différence entre les accolades ouvrantes et fermantes que vous avez rencontrées jusqu'à présent.Lorsque votre état est 0, cela signifie que les accolades correspondent à l'intérieur de la sous-chaîne actuelle.
L'astuce est que vous ne déconstruisez pas et ne reconstruisez pas la chaîne de cette façon, cela devrait donc être plus rapide.
En outre, vous pourriez gagner en performances même avec votre algorithme actuel en utilisant du texte paresseux - le concat
la fonction fonctionnerait plus efficacement.