Python中urlencode编码与Java结果不一致的问题如何解决?

我从文件里面读取一个 wav 文件然后,通过 urlencode 塞到 json 里面去,python 的 urllib.urlencode()出来的结果如下:

RIFF%AC%89%03%00WAVEfmt+

但是用 java 的 UrlEncoder 去编码结果却变成下面这样:

RIFF%C2%AC%C2%89%03%00WAVEfmt+

明显的 java 的 UrlEncoder 在 0xAC 和 0x89 这两个特殊字符前加上了%C2 这个字符,如果我想要用 java 得到 python 的结果这个要怎么做呢?


Python中urlencode编码与Java结果不一致的问题如何解决?

13 回复

……先 base64 encode 再 urlencode ;


这个问题通常是因为Python的urllib.parse.urlencode和Java的URLEncoder.encode在处理空格和特殊字符时有默认差异。

关键点在于:Python的urlencode默认将空格编码为%20,而Java的URLEncoder默认将空格编码为+

这是HTTP协议历史遗留问题:+%20在查询字符串中都表示空格,但不同环境默认不同。

解决方案:

  1. 统一使用%20(推荐) - 在Java端指定编码空格为%20:
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

String encoded = URLEncoder.encode("hello world", StandardCharsets.UTF_8)
                          .replace("+", "%20");
  1. 统一使用+ - 在Python端替换%20为+:
from urllib.parse import urlencode

params = {'q': 'hello world'}
encoded = urlencode(params).replace("%20", "+")
  1. 更彻底的方案 - 使用相同的编码规则:
# Python端自定义编码函数
def custom_urlencode(params):
    from urllib.parse import quote, urlencode
    return '&'.join(f'{k}={quote(v, safe="").replace("%20", "+")}' 
                    for k, v in params.items())

建议: 在跨语言项目中明确约定统一使用%20编码空格。

编码问题
python 在 latin-1/gbk 下执行
java 在 utf-8 下执行

字符’¬’,它的 ASCII/Latin-1 为 AC,utf-8(bytes)是 C2AC

塞到 json 里用 urlencode 首先就错了。base64 足矣。
其次,问题的原因是编码。如 2 楼所言。

你要这么想:输入是 bin,要求输出是 string,用什么?当然用 base64 一步达成。
urlencode 输出是 string,但输入也是 string,并不符合你的场景需求,你还得为它做一步转换准备好入参。预先从 bin -> string 过程中,必然会引入新的因素:编码。
再说一下字符编码。并不是任意一个 bin 都能转成对应的 string 的。很多编码方式都有它自己的规则,例如 utf-8,对于 n 字节的符号( n > 1 ),第一个字节的前 n 位都设为 1,第 n + 1 位设为 0,后面字节的前两位一律设为 10。因此很容易构造(遭遇)一个非法的 bin 序列在转换时报错。还有,转换后的码点是不是一个合法字符?这是由码表说了算。码表上不存在的,有可能就作为非法字符忽略或者显示为框或者问号。
假设第一步转换凑巧没出错,还得考虑第一步转出特殊字符,第二步 urlencode 时会不会正常处理。例如\0、\r、\n。。。毕竟它的设计并不是计划用在这种场合。

实际上 urlencode 暗含了两个步骤:
将 str 编码为 bytes,这一步的编码存在分别 ;
将 bytes 编码为 hex,这一步都是相同的。

在 Java 中,建议用 URLEncoder.encode(str, encoding)来明确的指出选择了哪种字符编码,以免踩坑。而 URLEncoder.encode(str) 已经是官方不建议使用的写法,如果用的是 IntelliJ IDEA 这么写会受到警告。

同理,Python 中建议使用 encoding= 关键字参数来指明字符编码,否则会随系统环境设置而变化。

是不是有一个默认 safe encode

wave 文件不总是可以被理解为一个字符串,这个做法一开始就错了。

当作 bytes 读进来,然后 encode 成 string,再 urlencode。

其实应该是 post 的一个参数所以 urlencode 了

确实是不合理的设计,不过对方的服务器 API 设计成这样也没办法

确实没关系,我看错了,post 参数是放在 dictionary 里面,只是这写法像 json。

回到顶部