用Parsec正确地用字符串分割

时间:2012-11-17 14:30:29

标签: haskell parsec

以下代码输出Right ["1<!>2<!>3"],但我需要Right ["1", "2", "3"]

import Text.ParserCombinators.Parsec

response = contents :: CharParser () [String]
  where
    contents = sepBy content contentDelimiter
    contentDelimiter = string "<!>"
    content = many anyChar

main = do
  putStrLn $ show $ parse response "Response" "1<!>2<!>3"

我认为这里的问题是content解析器在sepBy测试分隔符之前消耗所有输入。所以,我的问题是:

  1. 我的假设是否正确?如果没有,我犯了什么错误?

  2. 您会为这样的问题推荐什么解决方案? (使用Parsec)

  3. * content必须匹配任何不包含分隔符的字符串。 1<!>2<!>3只是一个例子,它可以是dslkf\n><!>dsf<!>3或其他

2 个答案:

答案 0 :(得分:8)

对于您的第一个示例,您将替换

content = many anyChar

content = many digit

这样内容的解析器就不会错误地匹配分隔符。

也许您希望匹配的不仅仅是数字,但即便如此,我建议您仔细考虑{em> 在<!>之间有效,并编写一个解析器。

<强>为什么吗
一旦你有一个非常好的内容解析器,你的响应定义将是完美的。这样,您的内容可以包含mystring = "hello<!>mum",而不会被顶级解析器切断 - 低级stringLiteral解析器会占用整个"hello<!>mum",顶级解析器将永远不会看到{{1}正确无误地包含在其中。

<强>通常,...
在大多数解析情况下,最好清楚地了解内容中允许的内容,并仅解析内容,原因有三:

  • 可重用性(您可以在更大的解析器中使用它)
  • 正确性
  • 通常效率 - 如果你避免过多的预测,你的解析器运行得更快。

可重用性很重要。目前,如果您使用的解析器只是在<!>上拆分并吃掉其他所有内容,那么它肯定会占用整个输入,并且您将无法再进行解析。

<强>自下而上
您的解析器应该从头开始工作 - 您在评论中将此描述为“将解析器从特定堆叠到一般”。

最容易按顺序编写它们以便于测试,因此首先在<!>之前stringChar之前写stringLiteral然后member匹配array之前的object } json之前的content然后response。你可以让他们一路上递归地互相呼叫。然后,您可以使用parseTest来测试每个小家伙;在ghci中键入parseTest response "1<!>2<!>3"比重写main和编译更快。

热门向下吗
自上而下编写解析器并没有错,只是更难。你可以写

response = many $ content `sepBy` contentSeparator
content = json <|> somethingElse
json = object <|> array
array = ...

但在你写完最小的解析器之前,没有什么是可测试的。

答案 1 :(得分:0)

如果不是'&lt;',我建议使用noneOf的解决方案和'!',也不是'&gt;'是您内容的一部分。

import Text.ParserCombinators.Parsec

response = contents :: CharParser () [String]
 where
   contents = sepBy content contentDelimiter
   contentDelimiter = string "<!>"
   content = many (noneOf ['<','!','>']) 

main = print $ parse response "Response" "1<!>2<!>3"