Python 3 中如何解决字典对字符串进行转义 - V2EX
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 7525 次点击
    这是一个创建于 2138 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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 格式了
    atx
        17
    atx  
       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     Solana     1076 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 30ms UTC 18:02 PVG 02:02 LAX 10:02 JFK 13:02
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86