这是使用基于StringScanner的自定义解析器的可靠答案。它允许空白是可选的,允许在列表中的最后一项之后使用尾随逗号,并允许在最后一个字典键/值对之后省略分号。它允许最外层的项目是字典、数组或字符串。它实际上允许任何类型的合法字符串内容,包括括号和花括号以及转义文本,例如\n
.
在行动中看到:
p parse('{ "array" = ( "1", "2", ( "3", "4" ) ); "hash"={ "key"={ "more"="oh}]yes;!"; }; }; }')
#=> {"array"=>["1", "2", ["3", "4"]], "hash"=>{"key"=>{"more"=>"oh}]yes;!"}}}
puts parse('("Escaped \"Quotes\" Allowed", "And Unicode \u2623 OK")')
#=> Escaped "Quotes" Allowed
#=> And Unicode ☣ OK
编码:
require 'strscan'
def parse(str)
ss, getstr, getary, getdct = StringScanner.new(str)
getvalue = ->{
if ss.scan /\s*\{\s*/ then getdct[]
elsif ss.scan /\s*\(\s*/ then getary[]
elsif str = getstr[] then str
elsif ss.scan /\s*[)}]\s*/ then nil end
}
getstr = ->{
if str=ss.scan(/\s*"(?:[^"\\]|\\u\d+|\\.)*"\s*/i)
eval str.gsub(/([^\\](?:\\\\)*)#(?=[{@$])/,'\1\#')
end
}
getary = ->{
[].tap do |a|
while v=getvalue[]
a << v
ss.scan /\s*,\s*/
end
end
}
getdct = ->{
{}.tap do |h|
while key = getstr[]
ss.scan /\s*=\s*/
if value=getvalue[] then h[key]=value; ss.scan(/\s*;\s*/) end
end
end
end
}
getvalue[]
end
作为将来从头开始构建自己的解析器的替代方案,您可能还想研究Treetop Ruby 库。
编辑:我已经getstr
用一个应该防止在eval
. 有关更多详细信息,请参阅“评估不带插值的字符串”。在行动中看到:
@secret = "OH NO!"
$secret = "OH NO!"
@@secret = "OH NO!"
puts parse('"\"#{:NOT&&:very}\" bad. \u262E\n#@secret \\#$secret \\\\#@@secret"')