2014-07-08 3 views
1

Je dois écrire un analyseur qui analyse paires clé-valeur dans un fichier qui ressemble à ceci:analyseur fparsec clé-valeur ne parvient pas à analyser

as235 242kj25klj Pairs:A=a1|B=b1|C=c1

kjlkjlkjlkj Pairs:A=a2|B=b2|C=c2

Notez que les lignes contiennent des ordures, l'étiquette, puis les paires clé-valeur.

Le code F # que j'ai écrit est la suivante:

#r"FParsec.dll" 

open FParsec 

let parse keys label = 
    let pkey = keys |> Seq.map pstring |> choice 

    let pvalue = manyCharsTill anyChar (anyOf "|\n") 

    let ppair = pkey .>> (skipChar '=') .>>. pvalue 

    let ppairSeq = many ppair 

    let pline = skipManyTill anyChar (pstring label) 
       >>. ppairSeq .>> newline 

    let pfile = many (opt pline) |>> Seq.choose id 

    run pfile 
    >> function 
    | Success (result, _, _) -> result 
    | Failure (errorMsg, _, _) -> failwith errorMsg 

""" 
as235 242kj25klj Pairs:A=a1|B=b1|C=c1 

lkjlkjlkjlkj Pairs:A=a2|B=b2|C=c2 



""" 
|> parse ["A";"B";"C"] "Pairs:" 
|> List.ofSeq 
|> printfn "%A" 

Le résultat attendu est:

[[("A","a1"); "B","b1"; "C","c1"] 
[("A","a2"); "B","b2"; "C","c2"]] 

... mais je reçois l'erreur suivante:

System.Exception: Error: Error in Ln: 8 Col: 1 
Note: The error occurred at the end of the input stream. 
Expecting: any char or 'Pairs:' 

Des idées sur la façon dont je peux réparer cet analyseur?

Merci! MISE À JOUR: après le commentaire de Stephan j'ai essayé de le réparer mais sans succès. C'est une de mes dernières tentatives que je m'attendais à faire mais ce n'est pas le cas. Maintenant, le message d'erreur

let pkey = keys |> Seq.map pstring |> choice 

let pvalue = manyCharsTill anyChar (anyOf "|\n") 

let ppair = pkey .>> (skipChar '=') .>>. pvalue 

let ppairSeq = manyTill ppair newline 

let pnonEmptyLine = 
    skipManyTill anyChar (pstring label) 
    >>. ppairSeq 
    |>> Some 

let pemptyLine = spaces >>. newline >>% None 

let pline = pemptyLine <|> pnonEmptyLine 

let pfile = manyTill pline eof |>> Seq.choose id 

est:

Error in Ln: 2 Col: 5 

    as235 242kj25klj Pairs:A=a1|B=b1|C=c1 

    ^

Expecting: newline 
+1

L'analyseur 'pline' semble échouer après avoir consommé entrée, comme' anyChar' consomme aussi des sauts de ligne, ce qui est probablement pas ce que vous voulait. Notez que 'many (opt pline)' conduira éventuellement à une exception, car 'opt x' peut réussir sans consommer d'entrée. Pour résoudre ce problème, vous pouvez ignorer les lignes vides en tant qu'espace (de fin) ou votre analyseur de ligne vide doit consommer un saut de ligne. –

+0

Je pense que je comprends ce que tu veux dire mais je ne sais pas comment réparer l'analyseur. J'ai signalé ma tentative comme une mise à jour – vidi

Répondre

4

Un de mes collègues a trouvé la solution et je poste ici pour d'autres qui ont des problèmes similaires. Aussi l'analyseur est encore meilleur parce qu'il n'a pas besoin de l'ensemble de clés. J'utilise le côté gauche de « = » comme la clé et le côté droit en tant que valeur:

let parse label str = 
    let poperand = manyChars (noneOf "=|\n") 

    let ppair = poperand .>> skipChar '=' .>>. poperand 

    let ppairSeq = sepBy ppair (pchar '|') 

    let pLineWithPairs = skipManyTill anyChar (pstring label) >>. ppairSeq |>> Some 

    let pLineWithoutPairs = (restOfLine false) >>% None 

    let pLogLine = (attempt pLineWithPairs) <|> pLineWithoutPairs 

    let pfile = sepBy pLogLine newline |>> Seq.choose id 

    match run pfile str with 
    | Success (result, _, _) -> result 
    | Failure (errorMsg, _, _) -> sprintf "Error: %s" errorMsg |> failwith 
+1

Notez que votre analyseur 'pLineWithPairs' analysera heureusement plusieurs lignes (qui ne contiennent pas l'étiquette). Je recommande d'utiliser 'manySatisfyL' pour la définition de' poperand' pour améliorer les performances. Si vous voulez éviter l'allocation des valeurs de l'option 'Some x', vous pouvez analyser les lignes vides comme des espaces entre les lignes qui vous intéressent. –

Questions connexes