2014-07-08 140 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」選項值,則可以將空行解析爲您感興趣的行之間的「空白」。 –