MySQL 的编码问题

解决字符集为 utf8_general_ci 的表里无法存储表情符号的问题。

前几天朋友的小程序遇到点问题,同样的代码,有些人就是注册不上,有些人就没问题。让我帮忙看。

看代码应该没问题,我自己试也没问题,很诡异。后来我发现,说注册不上的一个截图里,昵称里有一个雨滴的符号。我们知道,传统的编码是没有表情符号的,表情符号是 Unicode 后期才加进去的,那么会不会是数据库字段的问题?看了一眼,Laravel 默认创建的数据库,字符类型是 utf8mb4_unicode_ci,而他们数据库是 utf8_general_ci,Google 一下,找到下面这篇文章:

為什麼MYSQL要設定用UTF8MB4編碼 UTF8MB4_UNICODE_CI

里面提到:

當資料庫需要儲存或處理以下資料:emoji (手機端常用的表情字符)

应该使用 utf8mb4_unicode_ci,因为它会用更多的空间存储字符。基本锁定是字符集的问题。然后看到梦康大的一篇博文:直接使用 mysql utf8 存储 超过三个字节的 emoji 表情 ( 不使用 utf8mb4 ),决定参考他的方案,毕竟改库改表不是小事情。

不过时过境迁,梦康文中的 func_overload 已经可以用 mb_strlen($str, '8bit') 来替代,所以最后的代码大约是这样的:

// 替换
protected function encodeEmoji($input) {
  $length = mb_strlen($input, 'utf-8');
  $result = '';

  for ($i = 0; $i < $length; $i++) {
    $tmp = mb_substr($input, $i, 1, 'utf-8');
    if (mb_strlen($tmp, '8bit') >= 4) {
      $result .= '[[Emoji:' . rawurlencode($tmp) . ']]';
    } else {
      $result .= $tmp;
    }
  }
  return $result;
}

// 替换回
protected function decodeEmoji($nickname) {
  return preg_replace_callback('~\[\[Emoji:(.*?)\]\]~', function ($matches) {
    return rawurldecode($matches[1]);
  }, $nickname);
}

所以说,程序员心态要保持年轻,步调要跟年轻人保持一致,这样才更容易发现新问题,所以我的昵称已经改成“肉山🎩”了。

作者: meathill

爱编程,爱旅游,爱吐槽。 今年的第一目标是成为一名优秀的讲师,做够 25 场直播,收集 1000 位听众! (12/25) 《Electron + Vue 实战开发》创作中……

欢迎吐槽,请勿装死