Python 3中的Python挑战级别17

Python 3中的Python挑战级别17,python,string,python-3.x,bytearray,Python,String,Python 3.x,Bytearray,我最近开始玩。虽然相当复杂,但所需的编码并不十分困难,这使得学习许多有用的模块非常有趣 我的问题是关于17级。我理解在收集饼干时按照第4级所需的线索进行操作的想法,这就是我所做的。但是,我无法对得到的字符串进行BZ2解压缩 我尝试了谷歌搜索,发现了一个不错的博客,里面有Python 2中的解决方案。具体来说,17级的一个是。通过分析这一点,我意识到我确实得到了正确的压缩字符串(来自cookies),并且它在Python 2中得到了正确的解压缩: bz2.decompress(urllib.unq

我最近开始玩。虽然相当复杂,但所需的编码并不十分困难,这使得学习许多有用的模块非常有趣

我的问题是关于17级。我理解在收集饼干时按照第4级所需的线索进行操作的想法,这就是我所做的。但是,我无法对得到的字符串进行BZ2解压缩

我尝试了谷歌搜索,发现了一个不错的博客,里面有Python 2中的解决方案。具体来说,17级的一个是。通过分析这一点,我意识到我确实得到了正确的压缩字符串(来自cookies),并且它在Python 2中得到了正确的解压缩:

bz2.decompress(urllib.unquote_plus(compressed))
但是,Python3中的
bz2.decompress
需要字节数组而不是字符串,但显然Python3对应于上述行:

bz2.decompress(urllib.parse.unquote_plus(message).encode("utf8"))
操作失败,错误:无效数据流。我尝试了上面的所有方法和一些变体,但没有成功

以下是我目前的(非工作)解决方案:

#!/usr/bin/env python3

"""
The Python Challenge #17: http://www.pythonchallenge.com/pc/return/romance.html

This is similar to #4 and it actually uses its solution. However, the key is in
the cookies. The page's cookie says: "you+should+have+followed+busynothing..."

So, we follow the chain from #4, using the word "busynothing" and
reading the cookies.
"""

import urllib.request, urllib.parse
import re
import bz2

nothing = "12345"
last_cookie = None
message = ""
while True:
    headers = dict()
    if last_cookie:
        headers["Cookie"] = last_cookie
    r = urllib.request.Request("http://www.pythonchallenge.com/pc/def/linkedlist.php?busynothing=" + nothing, headers=headers)
    with urllib.request.urlopen(r) as u:
        last_cookie = u.getheader("Set-Cookie")
        m = re.match(r"info=(.*?);", last_cookie)
        if m:
            message += m.group(1)
        text = u.read().decode("utf8")
        print("{} >>> {}".format(nothing, text))
        m = re.search(r"\d+$", text)
        try:
            nothing = str(int(m.group(0)))
        except Exception as e:
            print(e)
            break

print("Cookies message:", message)
print("Decoded:", bz2.decompress(urllib.parse.unquote_plus(message).encode("utf8")))
所以,我的问题是:解决上述问题的Python3解决方案是什么样子的?为什么我的解决方案不能按预期工作

我很清楚,这部分工作可以做得更好。我想要的是一个快速而肮脏的解决方案,所以我在这里的兴趣只是它能工作(为什么不能像我上面做的那样工作)。

您需要在这里使用。它不支持
+
到空间的映射,但使用
str.replace()
可以轻松解决这一问题:

然后它会很好地解压缩。您可以将生成的未压缩字符串解码为ASCII:

print("Decoded:", bz2.decompress(urllib.parse.unquote_to_bytes(message.replace('+', '%20'))).decode('ascii'))
演示使用不同的信息,我准备不泄露谜题:

>>> import bz2
>>> import urllib.parse
>>> another_message = 'BZh91AY%26SY%80%F4C%E8%00%00%02%13%80%40%00%04%00%22%E3%8C%00+%00%22%004%D0%40%D04%0C%B7%3B%E6h%B1AIM%3D%5E.%E4%8Ap%A1%21%01%E8%87%D0'
>>> bz2.decompress(urllib.parse.unquote_to_bytes(another_message.replace('+', '%20'))).decode('ascii')
'This is not the message'
或者,告诉
urllib.unquote_plus()
使用拉丁-1编码而不是UTF-8。
unquote_plus()
的默认错误处理程序设置为
'replace'
,因此您永远不会注意到原始数据无法解码为UTF-8,因此字节被U+FFFD替换字符替换,这是导致解压缩失败的原因。Latin-1将oe上的所有字节直接映射到前256个Unicode字符,因此您可以将其编码回原始字节:

>>> '\ufffd' in urllib.parse.unquote_plus(another_message)
True
>>> bz2.decompress(urllib.parse.unquote_plus(another_message, 'latin1').encode('latin1')).decode('ascii')
'This is not the message'

由于“播放早期Python挑战级别以解锁它”,所以被否决。您应该在帖子中包含问题定义。@Nsh:这是我的编辑。我是Python挑战赛的参与者,我想让一些事情让我感到意外。请注意,代码已经可以完全重现问题了。@Nsh:您是否确实尝试重现问题?不需要该级别的链接。@Nsh:该链接用于提供问题的上下文,并使寻找类似问题的人更容易找到该问题。问题本身就包含在问题本身中:我得到了字符串
消息
,这是正确的(与正在运行的Python 2解决方案相比),但它没有像在等价的Python 2代码中那样解压(问题中提供)。@MartijnPieters如果这被视为一个破坏者,我深表歉意。我认为这不是问题,因为解决方案已经存在(我只链接了一个,但也有其他的)。如果有更好的方法提供上下文,请随意建议或编辑。谢谢。只是为了确定:一旦我将消息解压缩为字符串,就无法正确转换为字节了?@VedranŠego:您也可以将
unquote_plus()
与拉丁语1编解码器一起使用,然后再次编码回拉丁语1。这会将字节一对一映射到Unicode代码点:
urllib.parse.unquote_plus(message'latin1')。encode'latin1')
我更喜欢这个。非常感谢。所以这就提出了一个问题。。。一个人到底怎么会知道怎么做@rschwieb:pythonChallenge早在python3普及之前就已经创建了;Python 2的
unquote_plus()
从不解码为Unicode文本。
>>> '\ufffd' in urllib.parse.unquote_plus(another_message)
True
>>> bz2.decompress(urllib.parse.unquote_plus(another_message, 'latin1').encode('latin1')).decode('ascii')
'This is not the message'