在Ruby中将十六进制的STDIN / ARGV / gets转换为ASCII

4

我的问题是如何将来自cmd ARGV或gets的STDIN从十六进制转换为ASCII码。

我知道如果我将十六进制字符串分配给变量,它将在打印时自动转换。

例如:

hex_var = "\x41\41\x41\41"
puts hex_var

结果将会是

AAAA

但我需要通过命令行(ARGV或gets)获取值。

比如我有以下代码:

s = ARGV

puts s
# another idea
puts s[0].gsub('x' , '\x')

然后我运行了。
ruby  gett.rb \x41\x41\x41\x41 

我收到了:

\x41\x41\x41\x41

有没有办法让它工作?

2个回答

1

这里有几个问题需要解决。第一个问题你已经尝试过解决,但我认为你的解决方案并不理想。你在命令行参数中传递的反斜杠被 shell 解析了,从未传递到 ruby 脚本中。如果你只是在脚本中做一个 gsub,根本没有必要传递它们。而且按照你的方式,任何 参数中的 'x' 都会被替换掉,即使它们并没有用于表示十六进制。如果可能的话,最好在参数中双重转义 \。没有上下文的情况下,很难说哪种方式实际上更好。

ruby gett.rb \\x41\\x41

这样ARGV实际上会得到'\x41\x41',更接近你想要的结果。

但是,它仍然不完全符合你的要求,因为ARGV参数是在没有表达式替换的情况下创建的(就像在单引号中一样)。所以Ruby正在转义那个\,尽管你不希望它这样做。基本上,你需要将其作为双引号中的内容重新评估。

eval('"%s"' % s)

其中 s 代表字符串。

将所有内容放在一起,你最终可能会得到以下任意一个:

# ruby gett.rb \x41\x41

ARGV.each do |s|
  s = s.gsub('x' , '\x')
  p eval('"%s"' % s)
end
# => "AA"

# ruby gett.rb \\x41\\x41

ARGV.each do |s|
  p eval('"%s"' % s)
end
# => "AA"

ARGV.each do |s| s = s.gsub('x' , '\x') p eval('"%s"' % s) end - KING SABRI
谢谢@farski,其实我不知道eval(新手)。它在我的情况下运作得很好。我需要多了解一下它。谢谢。 - KING SABRI
尝试运行 ruby gett.rb "#{system('touch ~/security-fail')}"。然后想一想如果有人使用 rm -rf 会发生什么... 除非你百分之百信任所有当前和未来的用户,否则最好不要使用 eval。甚至可能连这都不行。 - Lars Haugseth
@LarsHaugseth 谢谢Lars, 我仍然不太了解eval及其安全问题,但另一方面我会在自己的脚本中使用它。 - KING SABRI

1

在控制台中输入的反斜杠将被Shell解释,除非您连续输入两个反斜杠,否则它们不会进入您的Ruby脚本。在这种情况下,您的脚本将获得一个字面上的反斜杠,并且不会自动转换跟随这些反斜杠的十六进制字符代码。

如果您将脚本的最后一行替换为以下内容,则可以手动将这些转义代码转换为字符:

puts s.gsub(/\\x([[:xdigit:]]{1,2})/) { $1.hex.chr }

然后使用双反斜杠输入运行它:

$ ruby gett.rb \\x41\\x42\\x43
ABC

当通过gets或类似方法获取用户输入时,每个字符转义只需要用户输入一个反斜杠即可,因为这将作为字面反斜杠传递到您的脚本中,并且可以通过上述gsub调用正确处理。


解析命令行参数的另一种方法是让 shell 为您解释字符转义。如何操作取决于您使用的 shell。如果使用 bash,可以这样做:

$ echo $'\x41\x42\x43'
ABC
$ ruby -e 'puts ARGV' $'\x41\x42\x43'
ABC

谢谢@Lars,但我会使用长十六进制数,这样我就不必为每个字节应用反斜杠。对于第二种解决方案对我来说不起作用。我正在处理OptionParser类,当我打印选项的参数时,它再次打印十六进制数,在我的情况下,我必须将其转换为ASCII以便执行其他任务。我从你们那里得到了很好的信息,谢谢。 - KING SABRI

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接