簡體   English   中英

如何在Ruby中使用哈希鍵進行這些字符串替換?

[英]How do I make these string substitutions using hash keys in Ruby?

我有一堆用Python和Ruby處理過的JSON文件,看起來像這樣:

{
    "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_THING中外部括號之間的所有內容,這是不好的。

我想如果這行得通,我需要同時更改我的正則表達式和Ruby代碼,但是我不確定在哪里尋求更多幫助。 也許gsub不是適合此工作的工具。 有任何想法嗎?

我在Linux x86_64上使用Ruby 2.3.7。

使用帶有初始哈希值的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

這是一個可能的解決方案:

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}" ,那么您希望該模式僅捕獲FOOBAZ ,而不捕獲FOO} bar {BAZ
  • 對掃描結果進行分組,然后調用flatten ,是拒絕捕獲組之外的內容(例如,在這種情況下為{}字符)的簡便方法。 (與使用slice(1,-2)類的索引相比,這僅會使代碼的保密性降低一點!
  • my_config.keys.include?(placeholder)檢查這是否實際上是。 一個已知的值,所以您不要用nil代替東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM