如何在Ruby中使用哈希键进行这些字符串替换?
我有一堆JSON文件,用Python和Ruby处理,看起来像这样:如何在Ruby中使用哈希键进行这些字符串替换?,ruby,Ruby,我有一堆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" } 请注意,键的显示
{
"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"}
但我想让它更灵活,更易于维护。第一个“更好”的解决方案抛出了一个错误,这显然是因为slice对“\0”本身进行操作,而不是对匹配项进行操作,而且我不确定它是否会多次匹配。当前未注释的解决方案不起作用,因为第二部分似乎一次只处理一个字母,而不是像我预期的那样处理每个匹配,所以它只是删除了大括号中的内容。更糟糕的是,它删除了PYTHON_ONLY_东西中外部大括号之间的所有内容,这是不好的
我想如果这能奏效的话,我需要同时修改我的正则表达式和Ruby代码,但我不确定在哪里可以找到更多的帮助。或者gsub不是这个工作的合适工具。有什么想法吗
我在Linux x86_64上使用Ruby 2.3.7。与初始哈希一起使用以替换:
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"}
与替换的初始哈希一起使用:
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"}
下面是一个可能的解决方案:
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
- 在正则表达式中使用
,而不是[[^}]+
,意味着您不会在匹配的这一部分“吞咽”太多。例如,如果输入包含*
,那么您希望该模式只捕获“{FOO}bar{BAZ}”
和FOO
,而不是BAZ
FOO}bar{BAZ
- 将扫描结果分组,然后调用
,这是一种简单的方法,可以拒绝捕获组之外的内容,即在本例中是展平
和{
字符。(这只会使代码比使用}
之类的索引稍微不那么神秘!)切片(1,-2)
检查这是否是一个已知值,因此您不会将其替换为my_config.keys.include?(占位符)
nil
- 这里有一个可能的解决方案:
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
- 在正则表达式中使用
,而不是[[^}]+
,意味着您不会在匹配的这一部分“吞咽”太多。例如,如果输入包含*
,那么您希望该模式只捕获“{FOO}bar{BAZ}”
和FOO
,而不是BAZ
FOO}bar{BAZ
- 将扫描结果分组,然后调用
,这是一种简单的方法,可以拒绝捕获组之外的内容,即在本例中是展平
和{
字符。(这只会使代码比使用}
之类的索引稍微不那么神秘!)切片(1,-2)
检查这是否是一个已知值,因此您不会将其替换为my_config.keys.include?(占位符)
nil
nil
吗?这是无效的JSON@Sinstein啊,好消息!我看错了文档。显然我是Ruby的初学者。@engineersmnky修复了。我当场编造的,它没有被使用,所以不太重要。:)slice
的第二个参数是length。如果是负整数,它不总是返回一个nil
吗?这是无效的JSON@Sinstein啊,好消息!我看错了文档。显然我是Ruby的初学者。:@engineersmnky修复了。我当场编造的,没有用过,所以不太重要。:)接受了hash as replacements:)是的,没错……但是这个方法有一个稍微不那么混乱的正则表达式:)它也慢了很多,因为这需要对字符串进行N*M次传递,而不仅仅是N次。谢谢你!我非常感谢你的透彻解释和额外的错误检查。我很快就理解了它,它按原样处理了我的测试代码。accep是的,没错……但是这个方法有一个稍微不那么混乱的正则表达式:)它也慢了很多,因为这需要对字符串进行N*M次传递,而不仅仅是N次。谢谢你!我非常感谢你的透彻解释和额外的错误检查。我很快就理解了它,并且它按原样处理了我的测试代码。我最喜欢第二个答案!(但正如OP提到的ruby 2.3,他们可能无法充分利用这一点,正如您所说)非常好!我更喜欢将哈希转换为gsub的方法。不过,我有一个问题。为了与我的代码一起工作,我制作了map blockmy_config=my_config.map…(snip)…end.to_h
。这是您所期望的吗?它看起来有点混乱。通过map进行复制比使用每个_键然后使用gsub!进行就地修改更可取还是更常见(就像我以前做的那样)?谢谢!事实上,to_h
是有意的,对不起。实际上,相当于Hash#transform_values
的方法已经存在很长时间了:h.merge(h){| |,value,| |……}
。我最喜欢第二个答案!(但正如OP提到的ruby 2.3,他们可能无法充分利用这一点,正如你所说的那样)非常好!我更喜欢转换散列以传递给gsub的方法。不过,我有一个问题。为了使用我的代码,我制作了映射块my_config=my_config.map…(snip)…end.to_h
。这是您所期望的吗?它看起来有点混乱。通过map进行复制比使用每个_键然后使用gsub!进行就地修改更可取还是更常见(就像我以前做的那样)?谢谢!事实上,to_h
是有意的,对不起。实际上,相当于Hash#transform_values
的方法已经存在很长时间了:h.merge(h){| |,value,| |……}
。