MySQL中伪装的UTF-8

问题发现

最近在修改一个bug的时候,发现了一个离奇的问题:

 String[] str = list1.get(i).getList2().get(j).split("\n");
                 for (k = 0; k < str.length; k++) {
 ​
                     String a = str[k];
 ​
                     String b = userSubmitAnswer.getUserAnswer().get(i).getUserAnswer()[j];
 ​
                     System.out.println(a);
                     System.out.println(b);
                     System.out.println(a.equals(b));
 ​
                     if (a.equals(b)) {
 ​
                         fillSubtableResult.setFillScore(list1.get(i).getList1().get(j));
                         fillSubtableResult.setFillSingleSituationId(1);
                         score = score + fillSubtableResult.getFillScore();
                         break;
                     }
                 }

这是一部分填空题的判题逻辑,a为数据库中的答案,b为用户的提交。 这么看来是没有问题的。

但是,当a、b的输出一致时,a.equals(b)的输出结果为false

然后进行断点调试,发现字符串a的后面还跟了一个/r,导致两个字符串的byte数组都不一样。

后面检查数据库,发现数据库的编码为CHARSET = utf8 我用的是 UTF-8 编码的客户端,服务器也是 UTF-8 编码的,数据库也是,就连用户提交的这个字符串“也是合法的 UTF-8。

问题的症结在于,MySQL 的“utf8”实际上不是真正的 UTF-8。

“utf8”只支持每个字符最多三个字节,而真正的 UTF-8 是每个字符最多四个字节。

MySQL 一直没有修复这个 bug,他们在 2010 年发布了一个叫作“utf8mb4”的字符集,绕过了这个问题。

当然,他们并没有对新的字符集广而告之(可能是因为这个 bug 让他们觉得很尴尬),以致于现在网络上仍然在建议开发者使用“utf8”,但这些建议都是错误的。

解决方案

既然问题出在了编码问题,那么解决方法是什么?

首先,需要解决多余/r的同时,需要将两个字符串保持编码一致。

那么就将两个字符串打为byte数组,在重新编码,利用正则表达式去除/r,这样即可保持编码一致,解决问题。

byte[] aBytes = str[k].getBytes(StandardCharsets.UTF_8);
                     String a = null;
                     try {
                         a = new String(aBytes, "utf8");
//                         a.replace("\r","");
                         if (a != null) {
                             Pattern p = Pattern.compile("\\s*|\t|\r|\n");//这里因为数据库编码问题,导致切割后的字符串后面会多出一个 \r
                             Matcher m = p.matcher(a);
                             a = m.replaceAll("");
                         }

                     }catch (Exception e) {
                         e.printStackTrace();
                     }

                     byte[] bBytes = userSubmitAnswer.getUserAnswer().get(i).getUserAnswer()[j].getBytes(StandardCharsets.UTF_8);
                     try {
                         b = new String(bBytes, "utf8");
                         if (b != null) {
                             Pattern p = Pattern.compile("\\s+");//这里因为数据库编码问题,导致切割后的字符串后面会多出一个 \r
                             Matcher m = p.matcher(b);
                             b = m.replaceAll("");
                         }
                     }catch (Exception e) {
                         e.printStackTrace();
                     }

简单概括

  • MySQL 的“utf8mb4”是真正的“UTF-8”。

  • MySQL 的“utf8”是一种“专属的编码”,它能够编码的 Unicode 字符并不多。

我要在这里澄清一下:所有在使用“utf8”的 MySQL 和 MariaDB 用户都应该改用“utf8mb4”,永远都不要再使用“utf8”。

记住:永远不要在 MySQL 中使用 UTF-8

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值