详细原因请参考:关于Java中bytes到String的转换-阿里云开发者社区
(1)string和byte转换之间需要指定字符编码参数Charset.defaultCharset(),默认不指定的情况下,使用的是utf-8编码,所以一般情况下相互转换使用的都是同一种编码utf-8,byte和string之间的来回转换不会出现错误。如以下代码示例:
public static void main(String[] args) {
byte[] original2 = new byte[] {(byte) 0xef, (byte) 0x8f, (byte) 0xff};
byte[] transformed2 =
new String(original2, Charset.defaultCharset()).getBytes(Charset.defaultCharset());
System.out.println(Arrays.toString(original2));
System.out.println(Arrays.toString(transformed2));
System.out.println(Arrays.equals(original2, transformed2));
}
(2)指定的字符编码不一致,导致string和byte转换出现错误的场景
string转byte时,指定了使用GBK字符编码,byte转回string字符时,使用了默认的utf-8。
在A机器上,string转byte时,使用的默认字符编码与系统一致是GBK字符编码。
把byte数据发送给B机器处理,byte转string时,使用的默认字符编码与系统一致是utf-8字符编码
https://www.unicode.org/versions/Unicode13.0.0/ch03.pdf
数据处理经过
现在有个命令需要处理:set a b
处理过程是,通过jedis客户端发送string或者byte数据到redis,redis底层只存byte格式的数据。
具体流程
(1)使用jedis存string ==》默认使用utf-8转byte存储 ==》默认读取使用utf-8解码读取
(2)使用jedis存string ==》指定字符编码,把string转byte存储 ==》指定字符编码,从byte转string后读取数据
(3)使用任意字符编码把string转byte后,使用jedis存byte ==》与编码无关直接存储 ==》与编码无关直接读取
jedis指定编码规则的源码位置:redis.clients.jedis.Client#set(java.lang.String, java.lang.String)
public void set(String key, String value) {
this.set(SafeEncoder.encode(key), SafeEncoder.encode(value));
}
看看encode的方法,默认指定了UTF-8
public static byte[] encode(String str) {
try {
if (str == null) {
throw new JedisDataException("value sent to redis cannot be null");
} else {
return str.getBytes("UTF-8");
}
} catch (UnsupportedEncodingException var2) {
throw new JedisException(var2);
}
}
字符编码丢失的问题
参考上面的具体流程可知,底层redis的存储不考虑字符编码,只存最终的byte格式数据。
使用jedis客户端的过程,可以指定string转换byte时使用的字符编码,比如utf-8、gbk等等,但是使用的什么字符编码,这个不会随着数据本身存储到redis底层。可以理解在这个过程中,使用的字符编码规则是什么丢失了。
总结:只从底层的数据byte本身,无法知道用户使用的什么字符编码把string转换成byte,然后存进来的。所以在对redis底层的数据,做数据迁移的过程中,最好不要存在改动原始byte数据的动作。比如你如果使用了new string(byte)数据,那就默认使用了utf-8的编码对它进行了转换,可能导致数据转换错误。