utf8数据在mysql中看起来很好,但在rails中被破坏了

时间:2014-03-21 15:42:40

标签: mysql ruby-on-rails utf-8

我正在为一位正在使用mac的同事设置一个rails环境(如果相关的话)。我从我们的实时mysql数据库中删除了数据,并使用该数据创建了一个本地开发数据库。如果我打开mysql控制台,并查看其名称字段中具有扩展字符集字符的记录的数据,那么它看起来很好。但是,在rails控制台(以及在rails生成的网页中)中,编码被破坏:例如,用“ - ”代替endash。

我知道的唯一与之相关的rails配置选项是config / database.yml。我目前有这套:

encoding: utf8
collation: utf8_general_ci

这使得它在我的机器上工作正常。但就像我说它不适用于我同事的机器。任何人的想法?

编辑1:在实时服务器上,我复制数据FROM,字符集信息如下所示:

mysql> show variables like 'char%';
+--------------------------+----------------------------+
| Variable_name            | Value                      |
+--------------------------+----------------------------+
| character_set_client     | latin1                     | 
| character_set_connection | latin1                     | 
| character_set_database   | latin1                     | 
| character_set_filesystem | binary                     | 
| character_set_results    | latin1                     | 
| character_set_server     | latin1                     | 
| character_set_system     | utf8                       | 
| character_sets_dir       | /usr/share/mysql/charsets/ | 
+--------------------------+----------------------------+
编辑2:在回应@ eggyal的评论时,我已经完成了几个mysqldump,这一点非常具有启发性。这是第一次转储:

$ mysqldump -u root -h127.0.0.1  dbname lessons --where="id=79510"
-- MySQL dump 10.11
--
-- Host: 127.0.0.1    Database: e_learning_resource_v3
-- ------------------------------------------------------
-- Server version   5.0.32-Debian_7etch4-log

/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `lessons`
--

DROP TABLE IF EXISTS `lessons`;
CREATE TABLE `lessons` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `description` text,
  `user_id` int(11) default NULL,
  `created_at` datetime default NULL,
  `privacy` int(11) default '1',
  `is_official` tinyint(1) default '0',
  `is_readonly` tinyint(1) default NULL,
  `comments_allowed` tinyint(1) default NULL,
  `hours` int(11) default NULL,
  `sessions` int(11) default NULL,
  `updated_at` datetime default NULL,
  `custom_menu_swf` varchar(255) default NULL,
  `pupil_liked_at` datetime default NULL,
  `user_liked_at` datetime default NULL,
  `pupil_favorite_count` int(11) default '0',
  `user_favorite_count` int(11) default '0',
  `teacher_notes` text,
  `pupil_notes` text,
  PRIMARY KEY  (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `lessons`
--
-- WHERE:  id=79510

LOCK TABLES `lessons` WRITE;
/*!40000 ALTER TABLE `lessons` DISABLE KEYS */;
INSERT INTO `lessons` VALUES (79510,'Jazz–Man',NULL,NULL,'2014-04-03 12:08:05',1,0,NULL,NULL,NULL,NULL,'2014-04-03 12:08:05',NULL,NULL,NULL,0,0,NULL,NULL);
/*!40000 ALTER TABLE `lessons` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2014-04-03 11:16:42

所以,这只是一个直接的mysqldump,并且在“INSERT INTO lessons”行中它有一个破碎的角色(爵士乐 - 男人)。

我再做一些额外的选项,转储文件中的数据看起来还不错:

$ mysqldump -u root -h127.0.0.1  dbname lessons --extended-insert --single-transaction --default-character-set=latin1 --skip-set-charset --where="id=79510" 
-- MySQL dump 10.11
--
-- Host: 127.0.0.1    Database: e_learning_resource_v3
-- ------------------------------------------------------
-- Server version   5.0.32-Debian_7etch4-log
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;

--
-- Table structure for table `lessons`
--

DROP TABLE IF EXISTS `lessons`;
CREATE TABLE `lessons` (
  `id` int(11) NOT NULL auto_increment,
  `name` varchar(255) default NULL,
  `description` text,
  `user_id` int(11) default NULL,
  `created_at` datetime default NULL,
  `privacy` int(11) default '1',
  `is_official` tinyint(1) default '0',
  `is_readonly` tinyint(1) default NULL,
  `comments_allowed` tinyint(1) default NULL,
  `hours` int(11) default NULL,
  `sessions` int(11) default NULL,
  `updated_at` datetime default NULL,
  `custom_menu_swf` varchar(255) default NULL,
  `pupil_liked_at` datetime default NULL,
  `user_liked_at` datetime default NULL,
  `pupil_favorite_count` int(11) default '0',
  `user_favorite_count` int(11) default '0',
  `teacher_notes` text,
  `pupil_notes` text,
  PRIMARY KEY  (`id`),
  KEY `user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

--
-- Dumping data for table `lessons`
--
-- WHERE:  id=79510

LOCK TABLES `lessons` WRITE;
/*!40000 ALTER TABLE `lessons` DISABLE KEYS */;
INSERT INTO `lessons` VALUES (79510,'Jazz–Man',NULL,NULL,'2014-04-03 12:08:05',1,0,NULL,NULL,NULL,NULL,'2014-04-03 12:08:05',NULL,NULL,NULL,0,0,NULL,NULL);
/*!40000 ALTER TABLE `lessons` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

-- Dump completed on 2014-04-03 11:18:20

所以,看起来额外的选项可以解决这个问题:

--extended-insert --single-transaction --default-character-set=latin1 --skip-set-charset

2 个答案:

答案 0 :(得分:3)

当MySQL客户端与服务器交互时:

  1. 服务器仅接收任何文本字符串;客户之前会告诉它如何对这些文本进行编码。

  2. 如果服务器必须将该文本存储在表中,则必须将其转码为相关列的编码(如果不同)。

  3. 如果客户端随后想要检索此类文本,则服务器必须将其转码为客户端期望的编码。

  4. 如果客户端在步骤1和3中使用的编码是相同的(通常是这种情况,特别是当两种情况下的客户端是同一个应用程序时),那么它经常被忽视如果客户端使用的编码不是它所说的编码。例如,假设客户端告诉MySQL它将使用latin1,但实际上是在utf8中发送数据:

    • 字符串'Jazz–Man'0x4a617a7ae280934d616e的形式发送到UTF-8服务器。

    • MySQL,在Windows-1252中解码这些字节,理解它们代表字符串'Jazz–Man'

    • 要存储在utf8列中,MySQL会将字符串转码为其UTF-8编码0x4a617a7ac3a2e282ace2809c4d616e。这可以使用SELECT HEX(name) FROM lessons WHERE id=79510

    • 进行验证
    • 当客户端检索该值时,MySQL认为它需要latin1,因此转码为Windows-1252编码0x4a617a7ae280934d616e

    • 当客户端收到这些字节时,它会将它们解码为UTF-8,因此将字符串理解为'Jazz–Man'

    结论:客户端没有意识到任何错误。仅当另一个客户端(不会错误地将其UTF-8连接错误地作为latin1)尝试使用该表时,才会检测到问题。在您的情况下,这发生在mysqldump获得数据导出时;使用--default-character-set=latin1 --skip-set-charset选项有效地强制mysqldump以与应用程序相同的方式运行,因此最终得到了正确编码的数据。

    要解决您的问题,您必须:

    1. 配置您的应用程序,以便正确设置其MySQL连接字符集(例如,在encoding: utf8中为Rails设置config/database.yml;

    2. 重新编码数据库中的数据,例如UPDATE lessons SET name = BINARY CONVERT(name USING latin1)(请注意,必须为每个错误编码的文本列执行此操作)。

    3. 另请注意,您可能希望以原子方式执行这两个操作,这可能需要一些思考。

答案 1 :(得分:0)

我设法通过半意外来解决这个问题。当我试图导入已经使用与LATIN1相关的额外选项完成的sql数据时(请参阅我的OP上的编辑3),我收到有关LC_TYPE变量的错误消息(我没有记下这个确切的错误很遗憾)。一些谷歌搜索让我在他的〜/ .bash_profile文件中设置了以下变量:

export LC_CTYPE=en_GB.UTF-8
export LANG=en_GB.UTF-8

设置完成后,打开一个新的控制台选项卡,我就可以导入数据了。但是,它看起来仍然是错误的(虽然以不同于以前的方式:例如,其他一些混乱的角色取代了endash。)我挠了挠头,然后做了一段时间的其他事情。

现在,在他多次重启笔记本电脑之后(因为已经过了几个星期),这一切都神奇地起作用了。所以,我认为重新启动修复了它。所以,答案是,我认为,这是:

在rails config/database.yml文件

中设置这些选项
encoding: utf8
collation: utf8_general_ci

将这些环境变量添加到~/.bash_profile

export LC_CTYPE=en_GB.UTF-8
export LANG=en_GB.UTF-8

添加(或更改它们已经存在)这些选项到你的mysql配置(在这种情况下,/Applications/MAMP PRO/MAMP PRO.app/Contents/Resources/my.cnf但更常见的位置是/etc/mysql/my.cnf/etc/my.cnf - 寻找它与locate my.cnf

collation-server = utf8_unicode_ci
init-connect='SET NAMES utf8'
character-set-server = utf8

现在重启你的机器。

然后,当你执行mysqldump时,请确保使用这些选项(除了你拥有的其他选项之外)

--extended-insert --single-transaction --default-character-set=latin1 --skip-set-charset

其中一些可能没有必要,但我认为这对我来说都是必要的!

感谢所有评论过你的人。

相关问题