如何将数字中的字节转换为字符串? (数字的字符表示)

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 个字符,您首先需要将它们分成三个数字:0x000000610x000000620x00000063

对于最后一个数字,您想要的十六进制数字已经在正确的位置。但对于另外两个,您必须分别使用>> 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 中,如何将字节数组转换为十六进制数字字符串,同时保持前导零? [复制]

在 Java 中,如何将字节数组转换为十六进制数字字符串,同时保持前导零? [复制]

为啥字符串不能像数字一样直接编码

python如何将字符转换为数字

如何将表示为字符串的数字转换为浮点数

在python中将字符串转换为数字[关闭]