有与所看到的解析器组合Scala的“repsep”麻烦

问题描述:

请帮帮忙!我想建立一个解析器在UPnP protocol定义解析SSDP消息。 (见“发现”一节)有与所看到的解析器组合Scala的“repsep”麻烦

基本上它的HTTP行一个头,后跟名称:值对,最后一个空行。

后大约5000的组合,这是我最想的一个“应该”工作: *

 
class SsdpParser() extends JavaTokenParsers {

def fulldoc: Parser[SsdpMessage] = header ~ nameValuePairs <~ "\r\n" ^^ { case header ~ values => SsdpMessage(header, values) }

def nameValuePairs:Parser[List[(String, String)]] = repsep(nameValuePair, "\r\n")

def nameValuePair:Parser[(String, String)] = ( //name ("""[-a-zA-Z0-9.]*""".r <~ ":") //value ~ """[a-zA-Z0-9:/,_; [].-\"\'\?=]*""".r) ^^ { case name ~ value => (name, value) }

def header: Parser[SsdpType] = (notifyLine | okLine | mSearch) def notifyLine:Parser[SsdpType] = "NOTIFY * HTTP/1.1\r\n" ^^ (x =>Notify) def mSearch:Parser[SsdpType] = "M-SEARCH * HTTP/1.1\r\n" ^^ (x=>Search) def okLine:Parser[SsdpType] = "HTTP/1.1 200 OK\r\n" ^^ (x=>OK)

}

然而,它在运行时,它看起来像它扼流圈的第一个名称/值对

失败:[3.1]失败:字符串匹配正则表达式\r\n' expected but '发现

日期:星期一,2011年3月28日6时37分31秒GMT

^

它找到一个换行符,为什么不匹配?是的,我已经验证这两个字符是在说明书和接收数据的实际两者(a.k.a在UTF-8 0X0D 0X0A)。

采样数据转储是在这里: *

 
HTTP/1.1 200 OK 
Cache-Control: max-age=300 
Date: Mon, 28 Mar 2011 06:37:31 GMT 
Ext: 
Location: http://192.168.1.1:1780/InternetGatewayDevice.xml 
Server: POSIX UPnP/1.0 DD-WRT Linux/V24 
ST: urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1 
USN: uuid:01A0DDA7-7404-815B-63C4-539B920D5E56::urn:schemas-upnp-org:service:WANCommonInterfaceConfig:1

0000 00 50 8d b5 2b df 00 12 17 39 a2 17 08 00 45 00 .P..+....9....E. 0010 01 83 00 00 40 00 40 11 b5 f0 c0 a8 01 01 c0 a8 [email protected]@......... 0020 01 28 07 6c cf 07 01 6f 7d b8 48 54 54 50 2f 31 .(.l...o}.HTTP/1 0030 2e 31 20 32 30 30 20 4f 4b 0d 0a 43 61 63 68 65 .1 200 OK..Cache 0040 2d 43 6f 6e 74 72 6f 6c 3a 20 6d 61 78 2d 61 67 -Control: max-ag 0050 65 3d 33 30 30 0d 0a 44 61 74 65 3a 20 53 75 6e e=300..Date: Sun 0060 2c 20 32 37 20 4d 61 72 20 32 30 31 31 20 30 38 , 27 Mar 2011 08 0070 3a 34 37 3a 33 34 20 47 4d 54 0d 0a 45 78 74 3a :47:34 GMT..Ext: 0080 20 0d 0a 4c 6f 63 61 74 69 6f 6e 3a 20 68 74 74 ..Location: htt 0090 70 3a 2f 2f 31 39 32 2e 31 36 38 2e 31 2e 31 3a p://192.168.1.1: 00a0 31 37 38 30 2f 49 6e 74 65 72 6e 65 74 47 61 74 1780/InternetGat 00b0 65 77 61 79 44 65 76 69 63 65 2e 78 6d 6c 0d 0a ewayDevice.xml.. 00c0 53 65 72 76 65 72 3a 20 50 4f 53 49 58 20 55 50 Server: POSIX UP 00d0 6e 50 2f 31 2e 30 20 44 44 2d 57 52 54 20 4c 69 nP/1.0 DD-WRT Li 00e0 6e 75 78 2f 56 32 34 0d 0a 53 54 3a 20 75 72 6e nux/V24..ST: urn 00f0 3a 73 63 68 65 6d 61 73 2d 75 70 6e 70 2d 6f 72 :schemas-upnp-or 0100 67 3a 73 65 72 76 69 63 65 3a 57 41 4e 43 6f 6d g:service:WANCom 0110 6d 6f 6e 49 6e 74 65 72 66 61 63 65 43 6f 6e 66 monInterfaceConf 0120 69 67 3a 31 0d 0a 55 53 4e 3a 20 75 75 69 64 3a ig:1..USN: uuid: 0130 30 31 41 30 44 44 41 37 2d 37 34 30 34 2d 38 31 01A0DDA7-7404-81 0140 35 42 2d 36 33 43 34 2d 35 33 39 42 39 32 30 44 5B-63C4-539B920D 0150 35 45 35 36 3a 3a 75 72 6e 3a 73 63 68 65 6d 61 5E56::urn:schema 0160 73 2d 75 70 6e 70 2d 6f 72 67 3a 73 65 72 76 69 s-upnp-org:servi 0170 63 65 3a 57 41 4e 43 6f 6d 6d 6f 6e 49 6e 74 65 ce:WANCommonInte 0180 72 66 61 63 65 43 6f 6e 66 69 67 3a 31 0d 0a 0d rfaceConfig:1... 0190 0a

+0

你们摇滚很难!我很欣赏的反馈 – 2011-03-28 19:34:09

快速浏览后,提出三点建议:

  • 使用RegexParsers代替JavaTokenParsers应该是不够的,如果你不使用任何的解析器JavaTokenParser内每默认
  • RegexParsers跳过空白;覆盖skipWhitespace来改变这种行为
  • 的另一种选择,看看parboiled,它有一个类似的语法解析器组合,是相当有据可查的,但IMO是更多的生产准备,具有更好的性能和错误报告(免责声明:我工作与家伙背后parboiled)
+0

我已经修改这包括覆盖VAL skipWhiteSpace =假 现在我窒息的最后一行: '失败:[9.1]失败:字符串匹配正则表达式“\ Z”预期,但'发现' 所以...它找不到更多的字符,但不匹配“\ z”(又名输入结束)? – 2011-03-28 15:20:29

+0

以下是一个完整的工作示例:https://gist.github.com/890674。也许你没有使用'phrase'来解析完整的输入? – jrudolph 2011-03-28 15:39:32

+0

我正在使用parser.parseAll(parser.fulldoc,input)。我没有用过这句话,我会研究它。感谢您的帮助 – 2011-03-28 19:32:24

由于jrudolph说,RegexParsers(及其子类JavaTokenParsers)默认跳过空白。您可以选择通过覆盖skipWhitespace来避免跳过空格,并且您还可以通过覆盖受保护的val whiteSpace: Regex来告诉它您认为是空白的选项。

问题来源于此:

def nameValuePairs:Parser[List[(String, String)]] = repsep(nameValuePair, "\r\n") 

这里,\r\n被自动跳过,所以也没有找到。一旦你改变skipWhitespace,你有错误,因为没有在文件的结尾额外\r\n,所以它希望看到另一个nameValuePair

你可能有更好的运气与此:

def nameValuePairs:Parser[List[(String, String)]] = rep(nameValuePair <~ "\r\n") 

或者,你可能会完全删除\r\n,让解析器跳过空白。