Python 加密字符串以获得固定长度
假设我有以下可能的字符串值:Python 加密字符串以获得固定长度,python,django,url,encoding,compression,Python,Django,Url,Encoding,Compression,假设我有以下可能的字符串值: exp="110" exp="110-120" exp="110-120-211" 实际上,这是从URL获得的GET参数exp可能会变得非常大,因此我想将其缩短(加密)。不是因为安全原因,而是因为我不想让它长而难看 所以我想加密exp成为一个固定长度的短字符串,比如说15。有可能吗?大概是这样的: encrypt("110") results in "Ax1234B" encrypt("110-120") results in "85xHdjX" 顺便说
exp="110"
exp="110-120"
exp="110-120-211"
实际上,这是从URL获得的GET参数exp
可能会变得非常大,因此我想将其缩短(加密)。不是因为安全原因,而是因为我不想让它长而难看
所以我想加密exp
成为一个固定长度的短字符串,比如说15。有可能吗?大概是这样的:
encrypt("110") results in "Ax1234B"
encrypt("110-120") results in "85xHdjX"
顺便说一句,我正在使用python
编辑
忘了提到:我还需要一个
解密
函数。此外,我更喜欢标准python库中的解决方案,而不必安装新的软件包。如果安全性根本不是问题,您只想混淆和缩小,那么可以使用?它是用Javascript和Python实现的,因此您可以轻松地在前端对其进行编码,在后端对其进行解码
但是,这不会得到固定的长度,除非您故意将长度增加到比实际长度大的某个长度。字符串的长度是多少?如果它们足够长,您可以使用
zlib
(或标准库中的另一个压缩模块)压缩它们,然后在其上运行base64
>>> z = base64.encodestring(zlib.compress("123"))
>>> print z
eJwzNDIGAAEtAJc=
>>> zlib.decompress(base64.decodestring(z))
'123'
但是,这不会缩小字符串,除非它们很长(在我的测试中,原始字符串中大约有36个字符)。你也不能得到一个固定的长度,但我不相信有任何方法可以做到这一点。不,不可能以可逆的方式将任意大的数据集放入一个固定的长度中。您需要更好地定义要传递的内容 例如,如果:
- 从您的示例中,它看起来像一个数字列表
- 这些数字相对较小
- 不允许复制
如果这些是真的,您可以将列表作为位打包到一个固定大小的二进制流中,最大位数为最大位数,然后进行base64编码。这将是固定长度和可逆的。如果您想将URL转换为固定长度字符串;您可以使用哈希函数和数据库来检索给定哈希的url:
import base64
import hashlib
import sqlite3
db = sqlite3.connect('urls.sqlite3')
db.execute('''CREATE TABLE IF NOT EXISTS urls
(hash BLOB PRIMARY KEY, url TEXT)''')
def shorten(url):
h = sqlite3.Binary(hashlib.sha256(url.encode('ascii')).digest())
with db:
db.execute('INSERT OR IGNORE INTO urls VALUES (?, ?)', (h, url))
return base64.urlsafe_b64encode(h).decode('ascii')
def geturl(shortened_url):
h = sqlite3.Binary(base64.urlsafe_b64decode(shortened_url.encode('ascii')))
with db:
url = db.execute('SELECT url FROM urls WHERE hash=?', (h,)).fetchone()
if url is None:
raise KeyError(shortened_url)
return url[0]
例子
输出
无论输入URL有多长(或多短),输出总是具有相同的长度
对于具有良好算法的足够长的散列,对于生成的任何实际数量的url散列,冲突的概率(不同的url产生相同的散列)都非常低。不,不可能以可逆的方式将任意大的数据集放入固定长度。也许你需要重新定义什么是允许的。您当然可以缩短它,因为我们用来缩短的一种方法是压缩字符串,然后对其进行base64编码。这显然不是固定长度,但非常可逆。您试图通信的数据的域是什么?如果您想缩小一些,如果您没有任何安全要求,那么您谈论的是压缩。如果您想将压缩后的表单存储在特定的字符串中,那么您正在谈论编码。如果您不介意,我会相应地更改标记。base64本身实际上会增长字符串。我考虑的是base64,但随着字符串长度的增长,base64也会增长。我想可能还有另一种解决办法。@cmd-Hmm,没错;我想可以将字符串(似乎是数字和破折号)表示的数据转换为二进制,然后用base64编码,但这似乎是一个非常麻烦的问题。@MihaiZamfir您可以将一个表示放大到一个固定的极限,但不能将它缩小到一个固定的极限而不丢失数据。当然,有实际的压缩,但我不知道有多少压缩技术可以处理超小的字符串。值得一提的是,URL中晦涩难懂的查询字符串对于大多数主要网站来说都是事实。比如Youtube、亚马逊、谷歌……我不介意投反对票,但原因是什么?如何改进答案?我的猜测是,正如你提到的,当碰撞发生时,这不起作用。谁想依赖于某种可能不会破裂的东西,但当第一次碰撞真正发生时,解决方案就行不通了?@StephanRyer我可能低估了这种可能性在实践中有多低。在任何情况下,都可以很容易地检测到冲突,并根据特定任务提供解决方案。概率增加了需要加密的字符串的数量,因此,在不知道这个数字的情况下,实际上不可能对发生这种情况的总体可能性说任何话。@StephanRyer可能的sha256哈希数:
2**256
相比之下,以秒为单位的宇宙年龄很小:~10**21
。在宇宙存在的每一秒,你可以每秒生成数十亿个哈希,并且你只使用所有可能哈希中的一小部分。考虑到解决方案使用了DB,如果我们检测到非常不可能的冲突(只需删除代码中的“或忽略”),我们可以在许多可能的候选对象中使用一个无随机哈希。
urls = ["110", "110-120", "110-120-211"]
width = max(map(len, urls))
for url in urls:
slug = shorten(url)
assert url == geturl(slug)
print('{url:{width}} -> {slug}'.format(**vars()))
110 -> m9sq9nmSBKKZxgOZS45ADksf1iXv23QGbMhp_uQsnfM=
110-120 -> aKGvjidWggSkQ1wBnZoi5f67KlUS1pvoVyhX8Rd04P0=
110-120-211 -> C8LD7lCh5Tm8XCoWJep9OAfSnMikLU5lgQChe-wfQho=