【问题标题】:All possible combinations of selected character substitution in a string in rubyruby 字符串中选定字符替换的所有可能组合
【发布时间】:2014-02-27 11:31:03
【问题描述】:

我想知道是否有一种简单的方法可以在 ruby​​ 中以简单的方式完成所选字符替换的每个组合。

一个例子:

    string = "this is a test"
    subs = ['a'=>'@','i'=>'!','s'=>'$']
    subs.combination.each { |c|
        string.gsub c
    }

会产生

    "this is @ test"
    "th!s !s a test"
    "thi$ i$ a te$t"
    "th!s !s @ test"
    "thi$ i$ @ te$t"
    "th!$ !$ a te$t"
    "th!$ !$ @ te$t"

感谢您的帮助!

【问题讨论】:

  • 只是好奇,为什么要生成组合?
  • 我正在参加的计算机安全课程中的作业。我们要写一个密码破解程序,给定一个单词列表应该破解我们教授给我们的散列密码。上面已经实现了,尽管以一种非常丑陋的方式实现,每个组合都在脚本中硬编码。
  • 您是否确实需要您的预期输出,然后我需要在我的答案上做更多工作?

标签: ruby string combinations substitution gsub


【解决方案1】:

我会这样做:

string = "this is a test"
subs = {'a'=>'@','i'=>'!','s'=>'$'}

keys = subs.keys
combinations = 1.upto(subs.size).flat_map { |i| keys.combination(i).to_a }

combinations.each do |ary|
  new_string = string.dup
  ary.each { |c| new_string.gsub!(c,subs) }
  puts new_string
end

输出

this is @ test
th!s !s a test
thi$ i$ a te$t
th!s !s @ test
thi$ i$ @ te$t
th!$ !$ a te$t
th!$ !$ @ te$t

【讨论】:

  • +1 这与我在 wifi 中断之前输入的内容相似(现在在通勤巴士上)。我注意到你有一个未使用的变量 new
  • @MarkThomas 谢谢,我首先想到的是,我会在那里创建#map,因此new。之后我改变了我的决定,但忘记删除new 变量。现在我做到了。
  • sub! 使用哈希参数很有趣,但我认为您不需要将匹配模式转换为正则表达式,对吗?
  • @sawa 不,这是不必要的.. :-) 谢谢,就像我回复 Mark 一样。我改变了我的计划,忘记删除那些。无论如何也感谢您的评论。
  • @Abdo 我使用了sub 这就是为什么.. 需要把gsub 放在那里.. 感谢您的指点。我对它做了很多实验,然后我融合了从那里移除了一些部分。
【解决方案2】:
string = "this is a test"
subs = ['a'=>'@','i'=>'!','s'=>'$']

subs = subs.first.map(&:to_a)
1.upto(subs.length).each do |n|
  subs.combination(n).each do |a|
    p a.each_with_object(string.dup){|pair, s| s.gsub!(*pair)}
  end
end

【讨论】:

  • 如果我没记错的话 OP 做错了结构..['a'=>'@','i'=>'!','s'=>'$'].. 应该是Hash.. 对吧?
  • @ArupRakshit 这不是散列,它是一个包含散列的数组,省略了{}
  • 好的。它是一个哈希数组或只有哈希。
【解决方案3】:

我会使用String#gsub(pattern, hash) 执行以下操作:

string = "this is a test"
subs = {'a'=>'@','i'=>'!','s'=>'$'} # copied from @ArupRakshit
keys = subs.keys

及核心代码:

1.upto(keys.length).flat_map { |i| 
  keys.combination(i).flat_map { |c| string.gsub(/[#{c.join}]/, subs) } 
}

输出:

=> ["this is @ test",
 "th!s !s a test",
 "thi$ i$ a te$t",
 "th!s !s @ test",
 "thi$ i$ @ te$t",
 "th!$ !$ a te$t",
 "th!$ !$ @ te$t"]

【讨论】:

  • 不错的解决方案,Abdo,+1。我不知道gsub 可以将哈希作为参数。很高兴知道。
  • @CarySwoveland 是的!我们的解决方案可以组合成以下几种:keys.repeated_combination(keys.length).map { |c| string.gsub(/[#{c.uniq.join}]/, subs) }.uniq
  • 我喜欢这样。你会为我的版税提议什么?
  • @CarySwoveland 哈哈 :-)
【解决方案4】:

另一种方式:

string = "this is a test"
subs   = [{"a"=>"@"}, {"i"=>"!"}, {"s"=>"$"}]

subs.repeated_combination(subs.size)
    .map {|e| string.gsub(/./) {|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}}
    .uniq
  #=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!$ !$ @ te$t",
  #    "th!s !s a test", "th!$ !$ a te$t", thi$ i$ a te$t"]

解释:

a = subs.repeated_combination(subs.size)
  # Enumerator...
  a.to_a 
  # [[{"a"=>"@"},{"a"=>"@"},{"a"=>"@"}], [{"a"=>"@"},{"a"=>"@"},{"i"=>"!"}],
  #  [{"a"=>"@"},{"a"=>"@"},{"s"=>"$"}], [{"a"=>"@"},{"i"=>"!"},{"i"=>"!"}],
  #  [{"a"=>"@"},{"i"=>"!"},{"s"=>"$"}], [{"a"=>"@"},{"s"=>"$"},{"s"=>"$"}],
  #  [{"i"=>"!"},{"i"=>"!"},{"i"=>"!"}], [{"i"=>"!"},{"i"=>"!"},{"s"=>"$"}],
  #  [{"i"=>"!"},{"s"=>"$"},{"s"=>"$"}], [{"s"=>"$"},{"s"=>"$"},{"s"=>"$"}]]

b = a.map {|e| string.gsub(/./) {|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}}
  #=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!s !s @ test",
  #    "th!$ !$ @ te$t", "thi$ i$ @ te$t", "th!s !s a test", "th!$ !$ a te$t",
  #    "th!$ !$ a te$t", "thi$ i$ a te$t"]

要查看b 是如何计算的,请考虑a 中传递给块的第二个元素:

    e = [{"a"=>"@"},{"a"=>"@"},{"i"=>"!"}]

由于正则表达式,/./gsubstring 中的每个字符c 传递给块

    {|c| (g = e.find {|h| h.key?(c)}) ? g[c] : c}

e 进行搜索以确定三个散列中的任何一个是否以c 作为键。如果找到一个,即g,则将字符c替换为g[c];否则,字符保持不变。

注意e 的前两个元素是相同的。将第一行改为:

    subs.repeated_combination(subs.size).map(&:uniq)

但效率不是这种方法的优点之一。

回到主计算,最后一步是:

b.uniq
  #=> ["this is @ test", "th!s !s @ test", "thi$ i$ @ te$t", "th!$ !$ @ te$t",
  #    "th!s !s a test", "th!$ !$ a te$t", "thi$ i$ a te$t"]

【讨论】:

  • 哦,是的!很好地使用了repeat_combination! +1
【解决方案5】:

单线功能解决方案

string = "this is a test"
subs = {'a'=>'@','i'=>'!','s'=>'$'}

(1..subs.size).flat_map { |n| subs.keys.combination(n).to_a }.map { |c| string.gsub(/[#{c.join}]/, subs) }
# => ["this is @ test", "th!s !s a test", "thi$ i$ a te$t", "th!s !s @ test", "thi$ i$ @ te$t", "th!$ !$ a te$t", "th!$ !$ @ te$t"]

【讨论】:

  • 我的是单行...我只是缩进了它,你不需要滚动。
  • 我同意最好把它分成几行以获得可读性
  • 根据官方规定,为了避免横向滚动,多行显示就可以作为单行。我这样做的好方法是用 .map 开始第二行,位于 .flat_map 下方。 Ruby 不会有任何问题(尽管 IRB/Pry 会)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 2017-11-11
  • 2018-06-29
  • 2022-08-17
相关资源
最近更新 更多