2014-07-08 110 views
1

我必须写一个解析器解析键值对的,看起来像这样的文件:fparsec键值解析器无法解析

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

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

注意,行包含一些垃圾,标签,然后键值对。

,我写的F#代码如下:

#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" 

预期的结果是:

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

...而是我得到以下错误:

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:' 

关于如何修复此解析器的任何想法?

谢谢!

更新:史蒂芬的评论后,我试图修复它,但没有成功。这是我期望能够工作的最后一次尝试,但事实并非如此。

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 

现在的错误信息是:

Error in Ln: 2 Col: 5 

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

    ^

Expecting: newline 
+1

的'pline'解析器似乎消耗输入后失败,因为'anyChar'也消耗换行,这可能不是你通缉。请注意,“许多(opt pline)'最终会导致异常,因为'opt x'可以在不消耗输入的情况下成功。要解决这个问题,你可以跳过空行作为(尾随)空白,或者你的空行解析器需要实际消耗一个换行符。 –

+0

我想我理解你的意思,但我不知道如何修复解析器。我发布了我的尝试作为更新 – vidi

回答

4

我的一个同事找到了解决办法,我在这里发帖别人谁拥有类似的问题。此外,解析器甚至更好,因为它不需要密钥集。我使用的“=”的左侧键和右侧值:

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

请注意,您的'pLineWithPairs'解析器将愉快地解析多行(不包含标签)。我建议使用'manySatisfyL'来定义'poperand'来提高性能。如果您想要避免分配“Some x”选项值,则可以将空行解析为您感兴趣的行之间的“空白”。 –