我有一个由PHP序列化的值,需要在Clojure中对其进行解码。我使用这个图书馆来反序列化它;它使用英斯塔帕斯,它利用EBNF/ABNF符号来定义语法。作为参考,以下是完整的定义:
<S> = expr
<expr> = (string | integer | double | boolean | null | array)+
<digit> = #'[0-9]'
<number> = negative* (decimal-num | integer-num)
<negative> = '-'
<integer-num> = digit+
<decimal-num> = integer-num '.' integer-num
<zero-or-one> = '0'|'1'
size = digit+
key = (string | integer)
<val> = expr
array = <'a:'> <size> <':{'> (key val)+ <'}'> <';'>?
boolean = <'b:'> zero-or-one <';'>
null = <'N;'>
integer = <'i:'> number <';'>
double = <'d:'> number <';'>
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>我在这个库中发现了一个bug --它不能处理包含"字符的序列化字符串。
php > echo serialize('{"key":"value"}');
s:15:"{"key":"value"}";使用这个库进行反序列化,当它发现第二个"时,它就会崩溃。
> (deserialize-php "s:15:\"{\"key\":\"value\"}\";")
[:index 7]语法定义的这一行存在问题:
string = <'s:'> <size> <':\\\"'> #'([^\"]|\\.)*' <'\\\";'>您会注意到字符串定义排除了"字符。但是,这是不正确的,我可以在字符串中包含任何字符;大小才是最重要的。我不是BNF专家,所以我想找出我在这里的选择。
是否可以使用大小作为要抓取的正确字符数?如果这是不可能的,是否有人认为我可以调整语法定义以支持正确的解析?
发布于 2013-08-30 21:14:20
作为由Arthur Ulfeldt陈述,这个语法是不具有上下文无关,这是由于本代码字符串。尽管如此,它是一个简单的解析,只是不使用A/EBNF。例如,使用https://github.com/protoflex/parse-ez代替:
方便宏:
(defmacro tagged-sphp-expr [tag parser]
`(fn [] (between #(string ~(str tag ":")) #(~parser) #(string ";"))))其余:
(def sphp-integer (tagged-sphp-expr "i" integer))
(def sphp-decimal (tagged-sphp-expr "d" decimal))
(defn sphp-boolean []
(= \1 ((tagged-sphp-expr "b" #(chr-in "01")))))
(defn sphp-null [] (string "N;") :null)
(defn sphp-string []
(let [tag (string "s:")
size (integer)
open (no-trim #(string ":\""))
contents (read-n size)
close (string "\";")]
contents))
(declare sphp-array)
(defn sphp-expr []
(any #(sphp-integer) #(sphp-decimal) #(sphp-boolean) #(sphp-null) #(sphp-string) #(sphp-array)))
(defn sphp-key []
(any #(sphp-string) #(sphp-integer)))
(defn sphp-kv-pair []
(apply array-map (series #(sphp-key) #(sphp-expr))))
(defn sphp-array []
(let [size (between #(string "a:") #(integer) #(string ":{"))
contents (times size sphp-kv-pair)]
(chr \})
(attempt #(chr \;))
contents))测试:
(def test-str "i:1;d:2;s:16:\"{\"key\": \"value\"}\";a:2:{s:3:\"php\";s:3:\"sux\";s:3:\"clj\";s:3:\"rox\";};b:1;")
(println test-str)
;=> i:1;d:2;s:16:"{"key": "value"}";a:2:{s:3:"php";s:3:"sux";s:3:"clj";s:3:"rox";};b:1;
(parse #(multi* sphp-expr) test-str)
;=> [1 2.0 "{\"key\": \"value\"}" [{"php" "sux"} {"clj" "rox"}] true]发布于 2013-08-29 20:26:48
我相当肯定,您不能仅仅用EBNF解析器来编写这个语法,因为据我所理解,这种语法并不是无上下文的。
发布于 2013-08-30 14:30:02
我认为,在上下文无关语法中,最接近的是显式枚举所有预期长度前缀--类似于ABNF:
string = 's:0:"";' /
's:1:"' CHAR '";' /
's:2:"' 2CHAR '";' /
's:3:"' 3CHAR '";' / ...如果字符串的长度是有界的,这可能会很好地工作,但是对于任意大小的字符串显然不起作用。
否则,要正确处理任意长度的字符串,最好的选择可能是手动解析.幸运的是,对于这么大的语法,这应该不是一个太困难的任务。
https://stackoverflow.com/questions/18518499
复制相似问题