为什么Parsec的sepBy停止并且不解析所有元素?

时间:2018-01-01 10:30:40

标签: parsing haskell parsec

我正在尝试解析一些逗号分隔的字符串,该字符串可能包含也可能不包含带图像尺寸的字符串。例如"hello world, 300x300, good bye world"

我写了以下小程序:

import Text.Parsec
import qualified Text.Parsec.Text as PS

parseTestString :: Text -> [Maybe (Int, Int)]
parseTestString s = case parse dimensStringParser "" s of
                      Left _ -> [Nothing]
                      Right dimens -> dimens

dimensStringParser :: PS.Parser [Maybe (Int, Int)]
dimensStringParser = (optionMaybe dimensParser) `sepBy` (char ',')

dimensParser :: PS.Parser (Int, Int)
dimensParser = do
  w <- many1 digit
  char 'x'
  h <- many1 digit
  return (read w, read h)

main :: IO ()
main = do
  print $ parseTestString "300x300,40x40,5x5"
  print $ parseTestString "300x300,hello,5x5,6x6"

根据optionMaybe文档,如果无法解析,则会返回Nothing,因此我希望得到此输出:

[Just (300,300),Just (40,40),Just (5,5)]
[Just (300,300),Nothing, Just (5,5), Just (6,6)]

但我得到了:

[Just (300,300),Just (40,40),Just (5,5)]
[Just (300,300),Nothing]

即。解析在第一次失败后停止。所以我有两个问题:

  1. 为什么会这样?
  2. 如何为此案例编写正确的解析器?

2 个答案:

答案 0 :(得分:2)

为了回答这个问题,拿一张纸,记下输入,并作为一个愚蠢的解析器很方便。

我们从“300x300,hello,5x5,6x6”开始,我们当前的解析器是optionMaybe ...。我们的dimensParser是否正确解析维度?我们来看看:

  w <- many1 digit   -- yes, "300"
  char 'x'           -- yes, "x"
  h <- many1 digit   -- yes, "300"
  return (read w, read h) -- never fails

我们已经成功解析了第一个维度。下一个标记是,,因此sepBy也成功解析了它。接下来,我们尝试解析“hello”并失败:

 w <- many1 digit -- no. 'h' is not a digit. Stop

接下来,sepBy尝试解析,,但这是不可能的,因为下一个标记是'h',而不是,。因此,sepBy会停止。

我们还没有解析所有输入,但这实际上并不是必需的。如果您使用了

,则会收到正确的错误消息
parse (dimensStringParser <* eof)

无论哪种方式,如果您要放弃列表中不是维度的任何内容,您可以使用

dimensStringParser1 :: Parser (Maybe (Int, Int))
dimensStringParser1 = (Just <$> dimensParser) <|> (skipMany (noneOf ",") >> Nothing)

dimensStringParser = dimensStringParser1  `sepBy` char ','

答案 1 :(得分:1)

我猜错public void onCompleted(GraphResponse response) { Log.d("mylog", response.toString()); try { JSONObject json = new JSONObject(response.getRawResponse()); JSONArray jarray = json.getJSONArray("data"); for (int i = 0; i < jarray.length(); i++) { JSONObject jsonUser = jarray.getJSONObject(i); //get your values String fullName = jsonUser.getString("name"); String id = jsonUser.getString("id") JSONObject pictureData = jsonUser.getJSONObject("picture").getJSONObject("data"); Uri url = Uri.parse(pictureData.getString("url")); } } catch (JSONException e) { e.printStackTrace(); } } ,在输入optionMaybe dimensParser后,尝试"hello,..."。这失败了,因此dimensParser会使optionMaybe返回成功,并且不会消耗任何输入部分。

最后一部分是关键部分:在返回Nothing之后,要解析的输入字符串仍为Nothing

此时"hello,..."尝试解析失败的sepBy。因此,它推断列表已经结束,并终止输出列表,而不再消耗任何输入。

如果你想跳过其他实体,你需要消费&#34;消费&#34;返回char ','而不是Nothing的解析器。但是,该解析器需要知道要消耗多少:在您的情况下,直到逗号。

也许你需要一些像(未经测试的)

optionMaybe