V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
euzen
V2EX  ›  Python

Python 3 中如何解决字典对字符串进行转义

  •  
  •   euzen · 2020-01-21 11:17:04 +08:00 · 6707 次点击
    这是一个创建于 1549 天前的主题,其中的信息可能已经有所发展或是发生改变。
    str = "源代码"
    bytes = str.encode('utf-8')
    str2 = bytes.decode('iso-8859-1')
    dict = {"key": str2}
    print(bytes)
    print(str2)
    print(dict)

    运行结果:
    b'\xe6\xba\x90\xe4\xbb\xa3\xe7\xa0\x81'
    源代码
    {'key': 'æº\x90代ç\xa0\x81'}

    赋值给字典后,会对小于\xa1 的字节进行转义,当使用 request.post 进行文件上传时,files 参数中涉及中文文件名就无法正确提交,有什么办法可以解决?
    第 1 条附言  ·  2020-01-21 21:53:19 +08:00
    多谢楼下各位的指教。事实上我标题中对字典转议的“指控”是错误的,这只是__repr__和__str__的区别。
    事实上我的问题根源是 urllib3 低版本的问题,它会将包含中文的字符串提交到服务器时进行“清空”,升级到 1.25.7 后问题已得到解决。
    第 2 条附言  ·  2020-01-23 09:56:37 +08:00
    结合楼下多位朋友的答疑和网上的一些搜索,终于完满解决手头的工作。也对 requests.post 的用法有进一步了解。
    1,使用 proxies 参数,配合 burpSuite,Charles 等软件,可以清晰查看 pythone 提交的数据,跟浏览器提交的数据对比,对程序排错帮助巨大。
    2,request.post 的 data 参数,可以接受字典和字符串两种类型,作为字典时,中文是会自动编码的,变成%ef%8a 这种方式;而参数是以字符串方式提供时,是以原样提交的,需要自己作适配处理,之前搞出 X-Y 问题,主要盲点就是在这里。
    当时工作是模拟浏览器发送一个带附件的邮件到 OA 系统,附件和正文是分两步提交的。附件可以按字典类型提交,但内容是嵌套的字典 ,类似 postdict={”mess":{"title":"xxx","content":"aaabbb"},”sts":0}。
    如果直接用字典类型提交,后台无法正确解析,这也是使用了 burp 软件查看 python 提交数据才知道。这时需要将字典用 str()转为字符串,poststr=str(postdict),再将得到的字符串 encode 一下:poststr.encode("utf-8")这样处理一下即可。开始时将字符 decode 为 iso-8859-1, 纯粹是巧合得到相符的结果。
    3,对于字典提交和字符串提交,需要在 header 里使用不同的 Content-Type 来配合。
    字典:"Content-Type":"application/x-www-form-urlencoded"
    字符串:"Content-Type": "text/json"
    在我的案例里是需要这样操作的,不知道跟后台服务器有没有关系,这可能还要结合具体情况进行测试。
    38 条回复    2020-01-23 09:02:59 +08:00
    InkStone
        1
    InkStone  
       2020-01-21 11:23:41 +08:00
    这跟字典没什么关系吧。单纯是__repr__和__str__两个模式的输出不一样。

    你这个操作是什么意思?为什么要用 utf-8 编码,用 iso88591 解码?编码错误,传得上去才见鬼了吧
    marquina
        2
    marquina  
       2020-01-21 11:24:41 +08:00
    实在没看懂这是什么操作……iso-8859-1 又不包含中文
    zdnyp
        3
    zdnyp  
       2020-01-21 11:25:03 +08:00
    3.6 str2 和 dict['key']的结果是一样的

    编码转的是不是有点没必要?
    euzen
        4
    euzen  
    OP
       2020-01-21 11:31:50 +08:00
    @InkStone 服务器就是接受 iso-8859-1 编码,没有服务器方面的代码,不知道是怎么回事。也是试也好久解决这个中文的问题。
    euzen
        5
    euzen  
    OP
       2020-01-21 11:34:23 +08:00
    @marquina 将 ‘源代码’提交到服务器是出错的,必须提交'\xe6\xba\x90\xe4\xbb\xa3\xe7\xa0\x81' 才可以。
    euzen
        6
    euzen  
    OP
       2020-01-21 11:37:52 +08:00
    @zdnyp 对 python 还是新接触,既然 3.6 结果是一样,那么 3.7 是不是有什么参数,开关可以做到输出一样的结果?
    euzen
        7
    euzen  
    OP
       2020-01-21 11:42:24 +08:00
    重点不是编码,是为什么赋值给字典后,字典会进行转义。
    ungrown
        8
    ungrown  
       2020-01-21 11:44:18 +08:00 via Android
    编解码不一致真的是迷
    你哪怕统一转换成 base64 也不这样靠谱
    marquina
        9
    marquina  
       2020-01-21 11:45:27 +08:00
    @euzen 服务器只接受 iso-8859-1 编码就代表服务器不接受中文……这个时候要想给服务器中文,唯一的办法不就是让服务器支持 utf-8 等编码吗……
    jyyx
        10
    jyyx  
       2020-01-21 11:46:25 +08:00
    看下__repr__和 __str__的区别
    你把发出来的代码最后一行改成 print(dict["key"])
    Schalkiii
        11
    Schalkiii  
       2020-01-21 11:47:27 +08:00
    代码里面不要写中文啥事没有...
    euzen
        12
    euzen  
    OP
       2020-01-21 11:50:58 +08:00
    @marquina 我提交 iso-8859-1 的值上去,服务器会转换出中文,并非服务器不支持 utf-8,而且无法控制服务器行为。
    Akikiki
        13
    Akikiki  
       2020-01-21 11:51:29 +08:00
    为什么要用关键字做变量
    euzen
        14
    euzen  
    OP
       2020-01-21 11:55:10 +08:00
    @jyyx 噢,得到了相同的结果。也大概了解了一下__repr__和__str__。第一个解答也是切中要点的。

    但更让我迷茫了,看来是搞错了方向,要继续研究解决方案。
    imn1
        15
    imn1  
       2020-01-21 12:05:35 +08:00
    不是,你现在上传有问题吗?什么问题?
    你纠结转义干嘛?\x80~\xa0 是不可视字符,当然转义啦
    imn1
        16
    imn1  
       2020-01-21 12:08:44 +08:00
    你要上传的格式是什么?
    字典本身是不能上传的,字符串?那不就是 json 么? dict2json,根本不需要理会编码,中文都变成\uxxxx 格式了
    lc1450
        17
    lc1450  
       2020-01-21 12:40:02 +08:00
    我猜你需要这个

    ```
    >>> from urllib.parse import quote
    >>> quote("哈哈")
    '%E5%93%88%E5%93%88'
    >>> quote("哈哈",encoding='gbk')
    '%B9%FE%B9%FE'
    ```
    euzen
        18
    euzen  
    OP
       2020-01-21 13:21:14 +08:00
    @imn1 上传文件,使用 request.post,里面有个参数是 files,传一个字典进去即可,字典有三项,第一是文件名,第二是字节流,第三个是协议格式。如果我文件名是全英文的话没问题,中文则上传成功,但服务器那边解析不出中文文件名,最后结果出错了。
    euzen
        19
    euzen  
    OP
       2020-01-21 13:23:01 +08:00
    @lc1450 这种方案已经试过来,服务器上是原样出来的,比如 哈哈.txt ,按这样解码,提交上去后服务器显示 %E5%93%88%E5%93%88.txt ,并不是我想要的结果。
    also24
        20
    also24  
       2020-01-21 13:26:21 +08:00 via Android
    咦,昨天群里有个人在问这个,我直接复制下我的回答:


    「 暴走的熊猫: python2 用 requests 库上传文件时,如果文件名是中文,上传失败,度娘一圈给的原因是中文文件名被进行 RFC 2231 编码了,导致找不到文件,给的解决方法被都是改源码,大佬们有其他方法么? 」
    - - - - - - - - - - - - - - -
    翻了一下,这个似乎是 urllib3 的锅
    https://github.com/psf/requests/issues/4652
    https://github.com/urllib3/urllib3/issues/303

    而 urllib3 似乎已经在 1.25 版本里修好了这个问题
    https://github.com/urllib3/urllib3/pull/1492
    https://github.com/urllib3/urllib3/blob/master/CHANGES.rst#125-2019-04-22

    你要不要看下你的 pip 或者 venv 里的 urllib3 的版本号
    xuboying
        21
    xuboying  
       2020-01-21 13:32:04 +08:00
    æºä»£ç 
    {'key': 'æº\x90代ç\xa0\x81'}
    这两个内容有啥区别。。。

    这是你的 terminal 渲染的结果,如果你的 terminal 的 locale,( windows 下是 chcp 值) 不同,会用其他的字体渲染出其他的结果,就好比一个苹果放在中国会写成苹果,放在美国会写成 apple,本质是完全一样的东西。

    要担心的是 utf8 到 iso8859 的互转会不会丢失信息,毕竟你的服务器要按照 8865 的结果转回去。不知道是不是可逆的
    also24
        22
    also24  
       2020-01-21 13:34:41 +08:00 via Android
    刚睡醒,仔细看了下应该不是同一个问题,我再看看楼主到底想干啥…
    also24
        23
    also24  
       2020-01-21 13:48:59 +08:00   ❤️ 1
    认真的看了一下,楼主你这个问题没有描述清楚哈:

    > 服务器就是接受 iso-8859-1 编码
    这个是根据什么判定的?怎么测试出来的?

    > 必须提交'\xe6\xba\x90\xe4\xbb\xa3\xe7\xa0\x81' 才可以
    是说这样提交就可以正常使用?这个是 utf-8,不是 iso-8859-1 啊,和上一条的结论似乎不一样?

    > 使用 request.post
    指的是 requests.post 嘛?(少了 s )
    如果是的话,我现在怎么觉得和我一开始认为的是同一个问题了呢……
    如果是这样的话,那还是看一下 urllib3 的版本号


    BTW:
    赞同 @xuboying 关于最开始那几种展示无区别的看法。

    BBTW:
    我感觉楼主还没有把整个问题的完整情况搞清楚。
    建议使用抓包软件抓一下 requests 发出的原始请求,确认编码情况。
    zappos
        24
    zappos  
       2020-01-21 14:00:14 +08:00 via Android
    也就是说 requests 库不会对 multipart 格式的中文自动编码是吧。我忘了 multipart 是怎么搞得了,你可以随便传个文件看一看。

    还有就是不要用不同的编码 encode 和 decode。
    CRVV
        25
    CRVV  
       2020-01-21 14:23:52 +08:00   ❤️ 2
    显然是 XY problem,还没把 Y 问题说清楚

    原本的问题是服务器不认 python 传过去的汉字,然后楼主拿 encode decode 和 iso-8859-1 折腾了一下就成功了,以为是服务器只支持 iso-8859-1。
    但实际上显然不是这么回事,如果服务器只支持 iso-8859-1,那就不可能成功地把汉字传上去。
    但是楼主又没说到底是怎么把字符串传给服务器的,如果是下面这样,说明服务器不支持 \u4e2d 这种编码 unicode 的方法。json.dumps 有一些带默认值的参数,改一改可能服务器就支持了。

    >>> json.dumps('中'.encode('utf-8').decode('iso-8859-1'))
    '"\\u00e4\\u00b8\\u00ad"'
    >>> json.dumps('中')
    '"\\u4e2d"'

    至于后面的文件读不出来
    '源代码' 这个字符串用 utf-8 编码过后的结果,再用 iso-8859-1 reinterpret 出来,就不是原来的字符串了,新的字符串是 'æº\x90代ç\xa0\x81',如果你要读的文件是 '源代码',那当然读不到

    .encode('utf-8').decode('iso-8859-1') 是相当于 float x = 3.14159; int* y = (int*)&x; 这样的操作,正常情况下不会用到的。
    euzen
        26
    euzen  
    OP
       2020-01-21 16:36:50 +08:00
    @also24 urllib3 1.23.2
    我觉得也是你说的这个问题。
    euzen
        27
    euzen  
    OP
       2020-01-21 16:52:24 +08:00 via iPhone
    @also24 自己另外搭了个测试页面来验证,有中文名字的话,服务器端接收完全是空的。
    euzen
        28
    euzen  
    OP
       2020-01-21 16:55:15 +08:00 via iPhone
    @xuboying 是的,确认跟这个没关系,是我搞错方向了。
    euzen
        29
    euzen  
    OP
       2020-01-21 16:56:41 +08:00 via iPhone
    @CRVV 的确是开始没搞清楚,跟编码没关系。
    also24
        30
    also24  
       2020-01-21 16:58:40 +08:00
    @euzen #27
    那和 urllib3 这个有关的可能性就非常大了……

    可以直接抓包把原始的请求内容拿出来看一下,或者在服务端打印一下所有字段的信息。

    懒得追查的话也可以先把 urllib3 升级到 1.25 版本以上看看问题会不会自动消失。


    你这个问题真的是搞了个超大的 X-Y Problem 出来……
    euzen
        31
    euzen  
    OP
       2020-01-21 17:46:30 +08:00
    @also24 升级到 1.25.7,可以在测试服务器上看到文件名有数据了,虽然编码还是不对,但距离解决已不远,谢谢你的解答。前期纠结编码,主要是服务器不能管理,无法验证。
    also24
        32
    also24  
       2020-01-21 17:57:37 +08:00
    @euzen #31

    那就用我一直建议的抓包的方式,你 requests 配置一下 Proxies,然后用 Charles 之类的工具看一下发出去的原始请求的具体格式,避免一直黑箱状态下摸瞎改代码。

    https://2.python-requests.org/en/master/user/advanced/#proxies
    luoleng
        33
    luoleng  
       2020-01-21 21:39:28 +08:00
    想要啥效果?{'key': b'\xe6\xba\x90\xe4\xbb\xa3\xe7\xa0\x81'}还是{'key': '\xe6\xba\x90\xe4\xbb\xa3\xe7\xa0\x81'}
    euzen
        34
    euzen  
    OP
       2020-01-21 21:49:14 +08:00
    @luoleng 原方向是需要后者的结果,但现在经 @also24 指点,发现跟字典无关,是 urllib3 低版本的问题。同时我关于字典会转义的指控也是错的,并没有转义,只是__repr__和__str__的区别。
    euzen
        35
    euzen  
    OP
       2020-01-21 22:07:24 +08:00
    @also24 真是长见识了,requests 还可以使用 proxies,明天配合 burp 尝试一下。
    also24
        36
    also24  
       2020-01-21 22:41:51 +08:00
    @euzen #35
    哈哈哈哈,再多嘴两句哈,我觉得你查找问题的思路需要改进一下。

    不太建议上来就靠着直觉猜,应该先尽可能收集能收集到的信息(日志,原始请求等)。

    先搞清楚问题的全貌,才能对症下药,不然就很容易搞出这种 X-Y Problem,白费大量精力。
    yhyh
        37
    yhyh  
       2020-01-22 14:01:31 +08:00
    你这个问题 感觉有固定的方案可以解决,我奇葩的是 字典转 json, 而字典的值 有双引号
    哭了
    euzen
        38
    euzen  
    OP
       2020-01-23 09:02:59 +08:00
    @CRVV 关于编码的问题,摸索了一天,再细读你的回复,利益不少。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3230 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 658ms · UTC 13:31 · PVG 21:31 · LAX 06:31 · JFK 09:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.