python脚本转二进制
人类以许多不同的方式传递信息。 在互联网上,主要格式是文本,这就是您阅读本文的方式。 但是,Internet上还有其他数据,例如图像和声音文件等。 在您意识到HTTP / 1.1和SMTP是基于文本的协议之前,将图像在线发布或将文档附加到电子邮件似乎很容易。 通过此类协议传输的数据必须表示为ASCII文本的子集(具体而言,字符33至126)。
显示数字图像的计算机已经将数字图像编码为二进制数据。 换句话说,数字图像不像打印在纸上的物理照片:它是计算机速记的集合,无论您使用哪种图像查看器对其进行解码,无论该图像查看器是否为网络浏览器,照片编辑应用程序或任何可以显示图像的软件。
要将图像重新编码为ASCII,通常使用Base64,这是一种二进制到文本编码规则的系统,可以将二进制数据表示为ASCII字符串。 这是一个以webp格式保存在Base64中的黑色像素:
UklGRiYAAABXRUJQVlA4IBoAAAAwAQCdASoBAAEAAIAOJaQAA3AA/v9gGAAAAA==
本文介绍了二进制/文本转换器,它们最流行的实现以及使用可变字母的非标准方法。 这是理论上的原型,纯属学术研究,因为时间和空间的复杂性使其仅适用于小型文件(最大数十KB)。 但是,此实现允许您选择任何基数,而不依赖于2的幂(例如7或77)。
转换二进制文件
这样的转换器的主要目的是将二进制文件放入可通过有限范围的受支持符号在通道上发送的形式。 一个很好的例子是任何基于文本的网络协议,其中所有传输的二进制数据都必须可逆地转换为纯文本形式,并且数据中不包含控制符号。 从0到31的ASCII码被视为控制字符,并且在任何不允许端点传输0到255的完整八位字节(二进制)的逻辑通道上传输时,它们都会丢失。
RFC 4648 。 该RFC还描述了Base32和Base16作为可能的变体。 这里的关键是它们都具有相同的特征:它们都是2的幂。 支持的符号(代码)的范围越广,转换结果的空间利用率越高。 它将更大,但问题是更大。 例如,由于将三个输入(八位值)字节转换为四个输出(六位值,2 6 = 64)字节,Base64编码提供了大约33%的输出。 因此,比率始终为4/3; 也就是说,输出大1/3或33.(3)%。 实际上,Base32效率很低,因为它意味着将五个输入(八位值的位)字节转换为八个输出(五位值的位,2 5 = 32)字节,并且比率为8/5; 也就是说,输出增加了3/5或60%。 在这种情况下,很难考虑Base16的任何效率,因为它的输出大小要大100%(每个带有八个值位的字节由两个四个值位字节表示,也称为半字节 ,2 4 = 16)。 它甚至不是翻译,而只是十六进制视图中八位字节的表示。这些输入和输出字节比率是使用最小公倍数(LCM)为Base64 / 32/16编码计算的。 您可以计算它,为此,您需要另一个功能:最大公约数(GCD)。
- Base64(输入:八位,输出:六位):
- LCM(8,6)= 8 * 6 / GCD(8,6)= 24位
- 输入:24/8 = 3字节
- 输出:24/6 = 4字节
- 比例(输出/输入):4/3
- Base32(输入:八位,输出:五位):
- LCM(8,5)= 8 * 5 / GCD(8,5)= 40位
- 输入:40/8 = 5字节
- 输出:40/5 = 8字节
- 比例(输出/输入):8/5
- Base16(输入:八位,输出:四位):
- LCM(8,4)= 8 * 4 / GCD(8,4)= 8位
- 输入:8/8 = 1字节
- 输出:8/4 = 2字节
- 比例(输出/输入):2/1
字符集有限
如果信道只能发送几个(例如9个或17个)不同的符号怎么办? 也就是说,如果您有一个由256个符号的字母(一个正常的八位字节)表示的文件,则不受编码器或解码器的计算能力或存储限制的限制。 但是,如果您只能发送七个不同的符号而不是256个,该怎么办? Base64、32和16不适合这种情况。 如果只有七个符号可用于编码,则Base7是唯一可能的输出格式。
或者,如果某个信道要考虑传输的数据量该怎么办? 无论传输什么,Base64总是将数据增加33%。 例如,Base94仅将输出提高22%。
似乎Base94并不是限制。 如果前32个ASCII代码是控制字符,并且总共有256个代码,那么为什么您不能使用256-32 = 224个符号的字母呢? 原因:并非所有这224个ASCII码都具有可打印的字符。 通常,只有7位(0到127)被标准化,其余的(128到255)用于各种语言环境,例如Koi8-R,Windows-1251等。 这意味着在标准化范围内只有128-32 = 96可用。 此外,ASCII码32是空格字符,而127也不具有可见字符。 因此96-2为您提供了94个可打印字符,每个字符在所有可能的机器上都与它们的代码具有相同的关联。
使用这种编码方法,您实际上可以传输任何符号最基本的数据。 实际上,您甚至可以只使用烟雾信号发送照片!
使用此Python脚本
该解决方案非常简单,但是这种简单性包括很大的计算约束。 整个输入文件可以视为一个大数,基数为256。它可能是一个很大的数字,需要数千位。 然后,您只需要将这个大数字转换为其他基数。 而已。
Python 3使它变得更加简单! 通常,不同基准之间的转换是通过中间的Base10完成的。 好消息是,Python 3内置了对大数计算的支持(它与Int集成),并且Int类具有一种方法,该方法可以读取任意数量的字节,并以所需的字节序自动以大的Base10数字表示它们。 。 因此,所有这些复杂性仅需两行代码即可实现,这真是太了不起了!
with open('input_file', 'rb') as f:
in_data = int.from_bytes(f.read(), 'big')
在这里,变量in_data是Base10的大数字。 仅这两行就完成了大部分计算,并且大部分时间都花在了上面。 通过这种初始编码,您可以将数据转换为任何其他基数,这通常是使用普通的小十进制数字完成的。
这是一个示例脚本,我在GitHub存储库中不断对其进行更新。
#!/usr/bin/env python3
from
sys
import argv
from
math
import ceil
base
=
42
abc
=
'''!"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[ \] ^_`abcdefghijklmnopqrstuvwxyz{|}~'''
def to_base
( fn_src
, fn_dst
, base
= base
) :
out_data
=
[
]
# represent a file as a big decimal number
with
open
( fn_src
,
'rb'
)
as f:
in_data
=
int .
from_bytes
( f.
read
(
)
,
'big'
)
# convert a big decimal number to a baseN
d
, r
= in_data % base
, in_data // base
out_data.
append
( abc
[ d
]
)
while r:
d
, r
= r % base
, r // base
out_data.
append
( abc
[ d
]
)
# write a result as a string to a file
with
open
( fn_dst
,
'wb'
)
as f:
f.
write
(
'' .
join
( out_data
) .
encode
(
)
)
def from_base
( fn_src
, fn_dst
, base
= base
) :
out_data
=
0
# read one long string at once to memory
with
open
( fn_src
,
'rb'
)
as f:
in_data
= f.
read
(
) .
decode
(
)
# convert a big baseN number to decimal
for i
, ch
in
enumerate
( in_data
) :
out_data
= abc.
index
( ch
) *
( base**i
) + out_data
# write a big decimal number to a file as a sequence of bytes
with
open
( fn_dst
,
'wb'
)
as f:
f.
write
( out_data.
to_bytes
( ceil
( out_data.
bit_length
(
) /
8
)
,
'big'
)
)
def usage
(
) :
print
( f
'usage: {argv[0]} <-e|-d> src dst [base={base}]'
)
raise
SystemExit
(
1
)
def main
(
) :
if
len
( argv
)
==
5 :
base
=
int
( argv
[
4
]
)
elif
len
( argv
)
<
4 :
usage
(
)
if argv
[
1
]
not
in
(
'-e'
,
'-d'
) :
usage
(
)
elif argv
[
1
]
==
'-e' :
to_base
( argv
[
2
]
, argv
[
3
]
, base
)
elif argv
[
1
]
==
'-d' :
from_base
( argv
[
2
]
, argv
[
3
]
, base
)
else :
usage
(
)
if __name__
==
'__main__' :
main
(
)
这是一个较长的版本Base94对奥莱克西Tsvietnov的博客,并与作者授权发表。
python脚本转二进制