我有一堆JSON文件,在Python和Ruby中处理,看起来像这样:
{
"KEY1": "foo",
"KEY2": "bar",
"URL": "https://{KEY2}.com/{KEY1}",
"IMPORTANT_THING": "repos/{KEY1}",
"NOTE": "This thing is {KEY1}{KEY2}ed",
"PYTHON_ONLY_THING": "{}/test/{}.py"
}
请注意,密钥显示的顺序不一致,我宁愿不更改JSON。
这是我的测试代码,显示了我迄今为止尝试过的内容:
my_config = {"KEY1"=>"foo",
"KEY2"=>"bar",
"URL"=>"https://{KEY2}.com/{KEY1}",
"IMPORTANT_THING"=>"repos/{KEY1}",
"NOTE"=>"This thing is {KEY1}{KEY2}ed",
"PYTHON_ONLY_THING"=>"{}/test/{}.py"}
my_config.each_key do |key|
# Braindead, hard-coded solution that works:
# my_config[key].gsub!("{KEY1}", my_config["KEY1"])
# my_config[key].gsub!("{KEY2}", my_config["KEY2"])
# More flexible (if it would work):
# my_config[key].gsub!(/{.*}/, my_config['\0'.slice(1,-2)])
my_config[key].gsub!(/{.*}/) {|s| my_config[s.slice(1,-2)]}
end
puts my_config
我现在正在使用braindead解决方案,它会产生预期的输出:
{"KEY1"=>"foo", "KEY2"=>"bar", "URL"=>"https://bar.com/foo", "IMPORTANT_THING"=>"repos/foo", "NOTE"=>"This thing is foobared", "PYTHON_ONLY_THING"=>"{}/test/{}.py"}
但我想让它更灵活,更易于维护。第一个"更好"解决方案显然会抛出错误,因为切片操作' \ 0'本身而不是匹配,加上我不确定它会不止一次匹配。当前没有注释的解决方案不起作用,因为第二部分似乎一次只能操作一个字母,而不是像我预期的那样每个匹配,所以它只是删除花括号中的东西。更糟糕的是,它删除了PYTHON_ONLY_THING中外括号之间的所有内容,这是不行的。
我想我需要更改我的正则表达式和Ruby代码,如果它能够工作,但我不知道在哪里寻求更多的帮助。或者也许gsub不是这项工作的正确工具。有什么想法吗?
我在Linux x86_64上使用Ruby 2.3.7。
答案 0 :(得分:4)
使用String#gsub
和替换的初始哈希:
my_config.map do |k, v|
[
k,
v.gsub(/(?<={)[^}]+(?=})/, my_config).gsub(/{(?!})|(?<!{)}/, '')
]
end.to_h
#⇒ {"KEY1"=>"foo",
# "KEY2"=>"bar",
# "URL"=>"https://bar.com/foo",
# "IMPORTANT_THING"=>"repos/foo",
# "NOTE"=>"This thing is foobared",
# "PYTHON_ONLY_THING"=>"{}/test/{}.py"}
从Ruby 2.4(或使用Rails)开始,使用Hash#transform_values
可以更简单。
如果您不喜欢第二个gsubbing,请事先转换哈希:
my_substs = my_config.map { |k, v| ["{#{k}}", v] }.to_h
my_config.map do |k, v|
[k, v.gsub(/{[^}]+}/, my_substs)]
end.to_h
答案 1 :(得分:2)
这是一个可能的解决方案:
my_config = {"KEY1"=>"foo",
"KEY2"=>"bar",
"URL"=>"https://{KEY2}.com/{KEY1}",
"IMPORTANT_THING"=>"repos/{KEY1}",
"NOTE"=>"This thing is {KEY1}{KEY2}ed",
"PYTHON_ONLY_THING"=>"{}/test/{}.py"}
my_config.each_key do |key|
placeholders = my_config[key].scan(/{([^}]+)}/).flatten
placeholders.each do |placeholder|
my_config[key].gsub!("{#{placeholder}}", my_config[placeholder]) if my_config.keys.include?(placeholder)
end
end
puts my_config
scan
,这将取代所有匹配,而不仅仅是第一场比赛。[[^}]+
,而不是.*
,意味着您不会吞下&#34;吞下&#34;在这部分比赛中太多了。例如,如果输入包含"{FOO} bar {BAZ}"
,那么您希望该模式仅捕获FOO
和BAZ
,而不是FOO} bar {BAZ
。flatten
,这是拒绝捕获组外部内容的简单方法,即在此情况下为{
和}
个字符。 (这使得代码比使用slice(1,-2)
等索引更加神秘!my_config.keys.include?(placeholder)
检查这是否确实存在。已知值,因此您不能使用nil
替换内容。