如何将数字中的字节转换为字符串? (数字的字符表示)
Posted
技术标签:
【中文标题】如何将数字中的字节转换为字符串? (数字的字符表示)【英文标题】:How to convert bytes in number into a string of characters? (character representation of a number) 【发布时间】:2017-08-17 01:16:39 【问题描述】:如何轻松转换数字,例如0x616263
,相当于以 10 为基数的 6382179
,通过将数字分成连续字节来转换为字符串?所以上面的例子应该转换成'abc'
。
我已经尝试过Array.pack,但无法弄清楚如何让它转换数字中的一个以上字节,例如[0x616263].pack("C*")
返回'c'
。
我也尝试过0x616263.to_s(256)
,但这会引发 ArgumentError: invalid radix。我猜它需要某种编码信息?
(注意:像 N
这样的包中的其他数据类型适用于我上面给出的示例,但只是因为它适合 4 个字节,所以例如 [0x616263646566].pack("N")
给出 cdef
,而不是 abcdef
)
这个问题有点像this one,但不是真的。另外,我想出了如何使用"abcde".unpack("c*").map|c| c.to_s(16).join("")
从字符串中获取十六进制表示字符串,它给出了'6162636465'
。我基本上想倒退。
我不认为这是 X-Y problem,但如果是的话 - 我正在尝试将使用 RSA 解码的数字转换为字符串。
感谢您的帮助。我对 Ruby 没有太多经验。我也对 Python 解决方案感兴趣(为了好玩),但我不知道为这个问题添加两种不同编程语言的标签是否正确。
【问题讨论】:
整数的最大值是多少? 大句子,可能大于 64 位 这些数字是从哪里来的?它们不只是一个字节流,它可以每隔 4 或 8 个字节定期切割并解释为一个整数数组吗? 该数字是来自“picoCTF”挑战之一 (2014.picoctf.com/problems) 的 RSA 解码密文。它是一个完整的字节序列,表示为一个数字。我不确定您将其视为字节流是什么意思 - 是否与我们下面的解决方案相同,只是将整数分解为字节? 好的,这就解释了为什么输出不是很标准。 【参考方案1】:我目前正在使用这个:
n = 0x616263
nhex = n.to_s(16)
nhexarr = nhex.scan(/.1,2/)
nhexarr = nhexarr.map |e| e.to_i(16)
out = nhexarr.pack("C*")
但希望有一种简洁/内置的方式来做到这一点,所以我暂时不接受这个答案。
【讨论】:
请注意,您的方法不适用于编码为0x0961
的"\ta"
【参考方案2】:
要将单个数字 0x00616263
转换为 3 个字符,您首先需要将它们分成三个数字:0x00000061
、0x00000062
和 0x00000063
。
对于最后一个数字,您想要的十六进制数字已经在正确的位置。但对于另外两个,您必须分别使用>> 16
和>> 8
进行位移。
然后,使用按位和去掉其他数字:
num1 = (0x616263 >> 16) & 0xFF
num2 = (0x616263 >> 8) & 0xFF
num3 = 0x616263 & 0xFF
对于角色,你可以这样做:
char1 = ((0x616263 >> 16) & 0xFF).chr
char2 = ((0x616263 >> 8) & 0xFF).chr
char3 = (0x616263 & 0xFF).chr
当然,按位运算不是非常 Ruby 风格的。其他人可能会提供更多类似 Ruby 的答案。
【讨论】:
谢谢。这就是我用其他语言做的方式,是的。可能在某种循环中同时对源编号和掩码进行位移,可能是 log16(number) 次以覆盖所有字符。 Eric,你要详细说明吗? 是的。我离得太远了,我什至不知道我以前在想什么。【参考方案3】:64 位整数
如果你的数字小于 2**64(8 字节),你可以:
将“big-endian unsigned long long”转换为 8 个字节 删除前导零字节红宝石
[0x616263].pack('Q>').sub(/\x00+/,'')
# "abc"
[0x616263646566].pack('Q>').sub(/\x00+/,'')
# "abcdef"
Python 2 和 3
在 Python 中,pack
返回字节,而不是字符串。你可以使用decode()
到convert bytes to a String :
import struct
import re
print(re.sub('\x00', '', struct.pack(">Q", 0x616263646566).decode()))
# abcdef
print(re.sub('\x00', '', struct.pack(">Q", 0x616263).decode()))
# abc
大数字
使用 gsub
如果您的号码不适合 8 个字节,您可以使用修改后的代码版本。如果第一个字节小于 10(例如对于“\t”),这将更短并且正确输出字符串:
def decode(int)
if int < 2**64
[int].pack('Q>').sub(/\x00+/, '')
else
nhex = int.to_s(16)
nhex = '0' + nhex if nhex.size.odd?
nhex.gsub(/../) |hh| hh.to_i(16).chr
end
end
puts decode(0x616263) == 'abc'
# true
puts decode(0x616263646566) == 'abcdef'
# true
puts decode(0x0961) == "\ta"
# true
puts decode(0x546869732073656e74656e63652069732077617920746f6f206c6f6e6720666f7220616e20496e743634)
# This sentence is way too long for an Int64
顺便说一下,这是相反的方法:
def encode(str)
str.reverse.each_byte.with_index.map |b, i| b * 256**i .inject(:+)
end
您仍然应该检查您的 RSA 代码是否真的输出任意大数字或只是一个整数数组。
有班次
这是获得结果的另一种方法。它类似于@Nathan 的答案,但它适用于任何整数大小:
def decode(int)
a = []
while int>0
a << (int & 0xFF)
int >>= 8
end
a.reverse.pack('C*')
end
根据fruity
,它的速度是gsub
解决方案的两倍。
【讨论】:
感谢您的回答,我从中学到了很多。我想经典的按位方式即使在红宝石中也是最好的!我喜欢您在正向和反向方法中避免中间字符串操作的方式。以上是关于如何将数字中的字节转换为字符串? (数字的字符表示)的主要内容,如果未能解决你的问题,请参考以下文章
在 Java 中,如何将字节数组转换为十六进制数字字符串,同时保持前导零? [复制]