Данные UTF-8 в базе данных Latin1: можно ли их сохранить?
У меня есть приложение rails, которое получает данные с устройства Android. Я заметил, что некоторые данные на японском не сохранены правильно. Он отображается в виде буквенных вопросительных знаков (не алмазных) в клиенте MySQL и на веб-сайте rails.
Оказывается, база данных, которую я подключил к приложению rails, настроена на Latin1. Rails установлен в UTF-8.
Я много читал о кодировках символов, но все они упоминают, что данные как-то немного читаются. Мой, однако, только буквальные знаки вопроса. Попытка конвертировать данные в UTF-8 с использованием нескольких методов в Интернете ничего не меняет. Я подозреваю, что данные преобразуются в вопросительные знаки при записи в базу данных.
Пример вывода из консоли MySQL:
select * from foo where bar = "foobar";
+-------+------+------------------------+---------------------+---------------------+
| id | name | bar | created_at | updated_at |
+-------+------+------------------------+---------------------+---------------------+
| 24300 | ???? | foobar | 2012-01-23 05:04:22 | 2012-01-23 05:04:22 |
+-------+------+------------------------+---------------------+---------------------+
1 row in set (0.00 sec)
Исходные данные, полученные моим приложением rails от клиента Android:
name = 爆笑笑話
Эти входные данные были проверены на наличие в приложении rails перед сохранением в базе данных. Так что это не искажено в клиенте Android или во время передачи на сервер. Есть ли шанс, что я смогу вернуть эти данные? Или это полностью потеряно?
1 ответ
На самом деле очень легко думать, что данные кодируются одним способом, тогда как на самом деле они кодируются другим способом: это потому, что любая попытка прямого получения данных приведет к преобразованию сначала в набор символов вашего соединения с базой данных, а затем в набор символов вашего выходного носителя - поэтому вы должны сначала проверить фактическое кодирование ваших сохраненных данных через SELECT BINARY name FROM foo WHERE bar = 'foobar'
или же SELECT HEX(name) FROM foo WHERE bar = 'foobar'
,
Где персонаж 爆
ожидается, что вы, вероятно, найдете одну из следующих последовательностей байтов:
0xe78886
, указывая, что ваш столбец на самом деле содержит данные в кодировке UTF-8: это обычно происходит, когда для набора символов соединения с базой данных, над которым был первоначально вставлен текст, было установлено значениеlatin1
но на самом деле данные в кодировке UTF-8 были отправлены.Вы должны видеть
?
символы при извлечении данных, потому что что-то между хранилищем данных и дисплеем не может перекодировать эти байты (однако, учитывая, что MySQL считает, что они представляют爆
и эти символы, вероятно, доступны в большинстве наборов символов, маловероятно, что они встречаются в самом MySQL (если вы явно не корректируете информацию о кодировке во время поиска).В любом случае, если это так, вам нужно удалить информацию о кодировке из столбца, а затем сообщить MySQL, что данные на самом деле кодируются как UTF-8. Как указано в
ALTER TABLE
Синтаксис:Предупреждение
CONVERT TO
Операция преобразует значения столбцов между наборами символов. Это не то, что вы хотите, если у вас есть столбец в одном наборе символов (например,latin1
) но сохраненные значения на самом деле используют другой несовместимый набор символов (например,utf8
). В этом случае вы должны сделать следующее для каждого такого столбца:ALTER TABLE t1 CHANGE c1 c1 BLOB; ALTER TABLE t1 CHANGE c1 c1 НАСТРОЙКА ХАРАКТЕРА ТЕКСТА utf8;
Причина, по которой это работает, заключается в том, что нет конвертации при конвертации в или из
BLOB
колонны.0x3f
указывает на то, что база данных действительно содержит буквенный символ?
и ваши исходные данные были потеряны: это не легко сделать, поскольку MySQL обычно выдает ошибку 1366, если неявное транскодирование приводит к потере данных. Возможно, в вашем выражении вставки было какое-то явное транскодирование?В этом случае вам необходимо преобразовать кодировку хранилища в подходящий формат, а затем обновить или заново вставить данные:
ALTER TABLE foo CONVERT TO utf8; UPDATE foo SET name = _utf8 '爆笑笑話' WHERE bar = 'foobar';