MySql 编码地狱
MySql encoding hell
我有一个网站运行快20年了,不幸的是我犯了一个错误,没有将HTML字符集与MySql字符集对齐,所以所有的我的数据似乎是双重编码的(我认为)或可能是 mojibaked,或两者兼而有之。也许你们中的一位专家可以为我解决这个问题。
在我继续之前,您应该知道我打算升级到 tomcat9 HTML5,使用 UTF8 字符和表情符号
With page pageEncoding="UTF-8" at the top of each page
request CharacterEncoding set to "UTF-8"
response CharacterEncoding set to "UTF-8"
and ContentType set to "text/html; charset=utf-8"
新的 MySql 数据库版本 8(最新版本)已经设置并位于同一台机器上。
包含所有记录的当前 (LIVE) MySql 版本是 5.6.19.
This is a small set of records that I see in workbench
这是上面table的设置:
创建 TABLE test
(
id
int(11) NOT NULL AUTO_INCREMENT,
txt
varchar(255) 整理 utf8_unicode_ci 默认 NULL,
主键 (id
)
) ENGINE=InnoDB AUTO_INCREMENT=19 默认字符集=utf8 COLLATE=utf8_unicode_ci;
MySql 5.6 variables
所有这些目前都可以在网页上完美呈现。
下面是上面的废话数据及其字节数组表示在一个页面上的渲染...
REC = don’t go breaking my heart
Bytes: 64 6f 6e ffffffe2 ffffff80 ffffff99 74 20 67 6f 20 62 72 65 61 6b 69 6e 67 20 6d 79 20 68 65 61 72 74 fffffff0 ffffff9f ffffff98 ffffff9b
REC =
Bytes: fffffff0 ffffff9f ffffff98 ffffff8d 20
REC = Haha......
Bytes: 48 61 68 61 2e 2e 2e 2e 2e 2e 20 fffffff0 ffffff9f ffffffa4 ffffffa4 fffffff0 ffffff9f ffffffa4 ffffffa4 fffffff0 ffffff9f ffffffa4 ffffffa4
REC = Mitteleuropäische Normalzeit
Bytes: 4d 69 74 74 65 6c 65 75 72 6f 70 ffffffc3 ffffffa4 69 73 63 68 65 20 4e 6f 72 6d 61 6c 7a 65 69 74
REC = Středoevropský letní čas
Bytes: 53 74 ffffffc5 ffffff99 65 64 6f 65 76 72 6f 70 73 6b ffffffc3 ffffffbd 20 6c 65 74 6e ffffffc3 ffffffad 20 ffffffc4 ffffff8d 61 73
REC = 中国标准时间
Bytes: ffffffe4 ffffffb8 ffffffad ffffffe5 ffffff9b ffffffbd ffffffe6 ffffffa0 ffffff87 ffffffe5 ffffff87 ffffff86 ffffffe6 ffffff97 ffffffb6 ffffffe9 ffffff97 ffffffb4
REC = Центральная Европа летнее время
Bytes: ffffffd0 ffffffa6 ffffffd0 ffffffb5 ffffffd0 ffffffbd ffffffd1 ffffff82 ffffffd1 ffffff80 ffffffd0 ffffffb0 ffffffd0 ffffffbb ffffffd1 ffffff8c ffffffd0 ffffffbd ffffffd0 ffffffb0 ffffffd1 ffffff8f 20 ffffffd0 ffffff95 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffbe ffffffd0 ffffffbf ffffffd0 ffffffb0 20 20 ffffffd0 ffffffbb ffffffd0 ffffffb5 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffb5 ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f
REC = Иркутск стандартное время
Bytes: ffffffd0 ffffff98 ffffffd1 ffffff80 ffffffd0 ffffffba ffffffd1 ffffff83 ffffffd1 ffffff82 ffffffd1 ffffff81 ffffffd0 ffffffba 20 20 ffffffd1 ffffff81 ffffffd1 ffffff82 ffffffd0 ffffffb0 ffffffd0 ffffffbd ffffffd0 ffffffb4 ffffffd0 ffffffb0 ffffffd1 ffffff80 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffbe ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f
REC = heure d’été d’Europe centrale
Bytes: 68 65 75 72 65 20 64 ffffffe2 ffffff80 ffffff99 ffffffc3 ffffffa9 74 ffffffc3 ffffffa9 20 64 ffffffe2 ffffff80 ffffff99 45 75 72 6f 70 65 20 63 65 6e 74 72 61 6c 65
REC = توقيت برازيليا الرسمي
Bytes: ffffffd8 ffffffaa ffffffd9 ffffff88 ffffffd9 ffffff82 ffffffd9 ffffff8a ffffffd8 ffffffaa 20 ffffffd8 ffffffa8 ffffffd8 ffffffb1 ffffffd8 ffffffa7 ffffffd8 ffffffb2 ffffffd9 ffffff8a ffffffd9 ffffff84 ffffffd9 ffffff8a ffffffd8 ffffffa7 20 ffffffd8 ffffffa7 ffffffd9 ffffff84 ffffffd8 ffffffb1 ffffffd8 ffffffb3 ffffffd9 ffffff85 ffffffd9 ffffff8a
REC = เวลาอินโดจีน
Bytes: ffffffe0 ffffffb9 ffffff80 ffffffe0 ffffffb8 ffffffa7 ffffffe0 ffffffb8 ffffffa5 ffffffe0 ffffffb8 ffffffb2 ffffffe0 ffffffb8 ffffffad ffffffe0 ffffffb8 ffffffb4 ffffffe0 ffffffb8 ffffff99 ffffffe0 ffffffb9 ffffff82 ffffffe0 ffffffb8 ffffff94 ffffffe0 ffffffb8 ffffff88 ffffffe0 ffffffb8 ffffffb5 ffffffe0 ffffffb8 ffffff99
REC = heure normale d’Afrique de l’Ouest
Bytes: 68 65 75 72 65 20 6e 6f 72 6d 61 6c 65 20 64 ffffffe2 ffffff80 ffffff99 41 66 72 69 71 75 65 20 64 65 20 6c ffffffe2 ffffff80 ffffff99 4f 75 65 73 74
REC = Центральная Америка летнее время
Bytes: ffffffd0 ffffffa6 ffffffd0 ffffffb5 ffffffd0 ffffffbd ffffffd1 ffffff82 ffffffd1 ffffff80 ffffffd0 ffffffb0 ffffffd0 ffffffbb ffffffd1 ffffff8c ffffffd0 ffffffbd ffffffd0 ffffffb0 ffffffd1 ffffff8f 20 ffffffd0 ffffff90 ffffffd0 ffffffbc ffffffd0 ffffffb5 ffffffd1 ffffff80 ffffffd0 ffffffb8 ffffffd0 ffffffba ffffffd0 ffffffb0 20 20 ffffffd0 ffffffbb ffffffd0 ffffffb5 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffb5 ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f
REC = Ora de vară a Europei de Est
Bytes: 4f 72 61 20 64 65 20 76 61 72 ffffffc4 ffffff83 20 61 20 45 75 72 6f 70 65 69 20 64 65 20 45 73 74
如您所见,数据在 UTF8 网页和旧的 Windows-1250 网页上也是可读的……但是其他任何东西都无法读取,我用谷歌搜索到死亡,并尝试了我在各个站点上找到的所有信息。是的,我看过 Rick James 的文档,但是 none 他的建议对我有用。
我已经尝试了所有我能想到的转换和转换的组合
最受欢迎的示例:
SELECT CONVERT(CAST(CONVERT(txt USING latin1) AS BINARY) USING UTF8) AS res FROM test;
解码成功'Mitteleuropäische Normalzeit',但其他记录全部变为NULL。
我也试过java的解码器
byte[] utf8Bytes = rec.getString("txt").getBytes("ISO-8859-1");
字符串结果 = 新字符串 (utf8Bytes, "UTF-8");
result 在网页上显示良好,但如果我将结果写入文件或将 result 发送到我的新数据库,它会变成又是垃圾
don’t go breaking my heart😛
ðŸ˜
Haha...... 🤤🤤🤤
Mitteleuropäische Normalzeit
StÅ™edoevropský letnà Äas
ä¸å›½æ ‡å‡†æ—¶é—´
Ð¦ÐµÐ½Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ Ð•Ð²Ñ€Ð¾Ð¿Ð° летнее времÑ
ИркутÑк Ñтандартное времÑ
heure d’été d’Europe centrale
توقيت برازيليا الرسمي
เวลาà¸à¸´à¸™à¹‚ดจีน
heure normale d’Afrique de l’Ouest
Ð¦ÐµÐ½Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ Ðмерика летнее времÑ
Ora de vară a Europei de Est
如你所见,我应该可以检索到数据,但还没有找到方法。
有人可以帮我解决这个问题吗?
记住,我只想将 unicode 数据以正确呈现的形式写入文件,或者将 unicode 数据以正确呈现的形式发送到我的新数据库。
鉴于现在没有人发布真正有用的解决方案,我想我会逆势而行。
如果您曾经遇到过这个问题并且想要使用 java8 提取双重编码数据并将其写入转储文件(如 csv 文件或 sql 文件),请尝试将其作为入门到您的项目...
// Create and fill a list of maps called recs
// Note: BaseMap is just my own class that extends Map, it has extensions like getString, getInt, etc
// I'm using column called "txt" to store some UTF8 test data
Path path = Paths.get("/my/file/name/here"); // <<< change this
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
for(int i=0; i<recs.size(); i++)
{
BaseMap rec = new BaseMap((Map)recs.get(i));
try {
byte[] doubleEncodedBytes = rec.getString("txt").getBytes("ISO-8859-1");
String decodedTxt = new String(doubleEncodedBytes, "UTF-8");
writer.append(decodedTxt);
writer.newLine();
// If you don't want to write to a file,
// you could instead execute on a connection to Database 2: INSERT INTO `schemaname`.`tablename` (columns...) VALUES (" decodedTxt ",....)
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
是的,这是一个缓慢的解决方案,但我阅读和尝试过的所有 MYSQL 解决方案都失败了,所以可以说这是我的“最终解决方案”
对于表情符号,您必须使用 MySQL 的 CHARACTER SET utf8mb4
。最好的方法是在连接期间建立它。第二好的是通过 SET NAMES utf8mb4;
.
SELECT UNHEX('646f6ee2809974207274f09f989b');
产量
don’t rt
适合你吗?但是——使用 SELECT HEX(col) ...
查看 col
中的内容;在您的代码显示十六进制之前 数据可能已被破坏 。
SELECT CONVERT(BINARY(CONVERT('d’Europe' USING latin1)) USING utf8mb4);
yields d’Europe mojibake to ut8 (or utf8mb4),
要进行更多调试,请执行
SELECT LENGTH(col), CHAR_LENGTH(col), col, HEX(col) FROM ...
如果是双重编码,我们可以从两个不同的长度来检测。
您显示了 CREATE TABLE
,但是加载数据的代码呢?以及倾倒它的代码?还是就地升级了?我的意思是,mojibake(等)可能是在升级期间发生的。
SELECT LENGTH('d’Europe'), CHAR_LENGTH('d’Europe'), 'd’Europe', HEX('d’Europe');
+----------------------+---------------------------+------------+----------------------+
| LENGTH('d’Europe') | CHAR_LENGTH('d’Europe') | d’Europe | HEX('d’Europe') |
+----------------------+---------------------------+------------+----------------------+
| 10 | 8 | d’Europe | 64E280994575726F7065 |
+----------------------+---------------------------+------------+----------------------+
“双重编码”的十六进制将是
64C3A2E282ACE284A24575726F7065
这显示了撤消它的最佳(?)方法:
SELECT CONVERT(BINARY(CONVERT(CONVERT(UNHEX('64C3A2E282ACE284A24575726F7065') USING utf8mb4) USING latin1)) USING utf8mb4);
+---------------------------------------------------------------------------------------------------------------------+
| CONVERT(BINARY(CONVERT(CONVERT(UNHEX('64C3A2E282ACE284A24575726F7065') USING utf8mb4) USING latin1)) USING utf8mb4) |
+---------------------------------------------------------------------------------------------------------------------+
| d’Europe |
+---------------------------------------------------------------------------------------------------------------------+
我浏览了您提供的其他行;他们似乎彼此一致。也就是说,使其中一个起作用的修复将适用于其他修复。 (一定要用表情符号检查一个。)
更多
这是一个硬汉。
Here's the first row from SELECT HEX(col)
'646F6EC3A2C280C2997420676F20627265616B696E67206D79206865617274C3B0C29FC298C29B'
for don’t go breaking my heart
让我从“正确的单引号”(’
) 开始,因为它似乎具有代表性。
Char UTF-8 If interpreted as latin1
’ E28099 ’
’ C3A2 C280 C299 You have this
’ C3A2 E282AC E284A2 Correct encoding
有些东西把 €
变成了 C280
。虽然反向工作“正确”,但它不是有效的 UTF-8 映射。我不知道那件事发生在哪里;我怀疑它是否在 MySQL 内。映射是在某些客户端完成的吗?
由此可见,MySQL不想使用C280
:
mysql> SELECT CAST(UNHEX('C3A2E282ACE284A2') AS char), CAST(UNHEX('C3A2C280C299') AS char);
+-----------------------------------------+-------------------------------------+
| CAST(UNHEX('C3A2E282ACE284A2') AS char) | CAST(UNHEX('C3A2C280C299') AS char) |
+-----------------------------------------+-------------------------------------+
| ’ | â |
+-----------------------------------------+-------------------------------------+
因此,我在 mysql 中没有看到 清理混乱的简单方法。但是,将 C280
转换为 80
.
相当简单
如果你能以某种方式得到 string don’t
,然后将 string 送回 MySQL,加上一个 CONVERT 表达式,您也许可以修复它。
直到大约十年前,C280
是黑客用来将顽皮的东西绕过浏览器的诡计的一部分。但是浏览器变得聪明了。
如果您能找到 C280
的来源,请提交软件的安全错误报告。并停止使用它。
如果是在 MySQL 20 年前,您可能一直在使用 latin1
版本 4.0(没有其他字符集)或从 latin1 不正确转换的 4.1。或者可能仍在使用 latin1,但使用 UTF-8 字符只是横跨 2-3 个 latin1 字符。那时,错误的 ALTER
导致“双重编码”,但您可能没有注意到它,因为 INSERT
期间的 mangling 大部分在 SELECT
.
期间未完成
MySQL 5.5(2010 年底)引入了可以正确处理表情符号的 utf8mb4。
(与此同时,表情符号看起来就像是被破解的双重编码。)
我有一个网站运行快20年了,不幸的是我犯了一个错误,没有将HTML字符集与MySql字符集对齐,所以所有的我的数据似乎是双重编码的(我认为)或可能是 mojibaked,或两者兼而有之。也许你们中的一位专家可以为我解决这个问题。
在我继续之前,您应该知道我打算升级到 tomcat9 HTML5,使用 UTF8 字符和表情符号
With page pageEncoding="UTF-8" at the top of each page request CharacterEncoding set to "UTF-8" response CharacterEncoding set to "UTF-8" and ContentType set to "text/html; charset=utf-8"
新的 MySql 数据库版本 8(最新版本)已经设置并位于同一台机器上。 包含所有记录的当前 (LIVE) MySql 版本是 5.6.19.
This is a small set of records that I see in workbench
这是上面table的设置:
创建 TABLE test
(
id
int(11) NOT NULL AUTO_INCREMENT,
txt
varchar(255) 整理 utf8_unicode_ci 默认 NULL,
主键 (id
)
) ENGINE=InnoDB AUTO_INCREMENT=19 默认字符集=utf8 COLLATE=utf8_unicode_ci;
MySql 5.6 variables
所有这些目前都可以在网页上完美呈现。 下面是上面的废话数据及其字节数组表示在一个页面上的渲染...
REC = don’t go breaking my heart Bytes: 64 6f 6e ffffffe2 ffffff80 ffffff99 74 20 67 6f 20 62 72 65 61 6b 69 6e 67 20 6d 79 20 68 65 61 72 74 fffffff0 ffffff9f ffffff98 ffffff9b REC = Bytes: fffffff0 ffffff9f ffffff98 ffffff8d 20 REC = Haha...... Bytes: 48 61 68 61 2e 2e 2e 2e 2e 2e 20 fffffff0 ffffff9f ffffffa4 ffffffa4 fffffff0 ffffff9f ffffffa4 ffffffa4 fffffff0 ffffff9f ffffffa4 ffffffa4 REC = Mitteleuropäische Normalzeit Bytes: 4d 69 74 74 65 6c 65 75 72 6f 70 ffffffc3 ffffffa4 69 73 63 68 65 20 4e 6f 72 6d 61 6c 7a 65 69 74 REC = Středoevropský letní čas Bytes: 53 74 ffffffc5 ffffff99 65 64 6f 65 76 72 6f 70 73 6b ffffffc3 ffffffbd 20 6c 65 74 6e ffffffc3 ffffffad 20 ffffffc4 ffffff8d 61 73 REC = 中国标准时间 Bytes: ffffffe4 ffffffb8 ffffffad ffffffe5 ffffff9b ffffffbd ffffffe6 ffffffa0 ffffff87 ffffffe5 ffffff87 ffffff86 ffffffe6 ffffff97 ffffffb6 ffffffe9 ffffff97 ffffffb4 REC = Центральная Европа летнее время Bytes: ffffffd0 ffffffa6 ffffffd0 ffffffb5 ffffffd0 ffffffbd ffffffd1 ffffff82 ffffffd1 ffffff80 ffffffd0 ffffffb0 ffffffd0 ffffffbb ffffffd1 ffffff8c ffffffd0 ffffffbd ffffffd0 ffffffb0 ffffffd1 ffffff8f 20 ffffffd0 ffffff95 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffbe ffffffd0 ffffffbf ffffffd0 ffffffb0 20 20 ffffffd0 ffffffbb ffffffd0 ffffffb5 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffb5 ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f REC = Иркутск стандартное время Bytes: ffffffd0 ffffff98 ffffffd1 ffffff80 ffffffd0 ffffffba ffffffd1 ffffff83 ffffffd1 ffffff82 ffffffd1 ffffff81 ffffffd0 ffffffba 20 20 ffffffd1 ffffff81 ffffffd1 ffffff82 ffffffd0 ffffffb0 ffffffd0 ffffffbd ffffffd0 ffffffb4 ffffffd0 ffffffb0 ffffffd1 ffffff80 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffbe ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f REC = heure d’été d’Europe centrale Bytes: 68 65 75 72 65 20 64 ffffffe2 ffffff80 ffffff99 ffffffc3 ffffffa9 74 ffffffc3 ffffffa9 20 64 ffffffe2 ffffff80 ffffff99 45 75 72 6f 70 65 20 63 65 6e 74 72 61 6c 65 REC = توقيت برازيليا الرسمي Bytes: ffffffd8 ffffffaa ffffffd9 ffffff88 ffffffd9 ffffff82 ffffffd9 ffffff8a ffffffd8 ffffffaa 20 ffffffd8 ffffffa8 ffffffd8 ffffffb1 ffffffd8 ffffffa7 ffffffd8 ffffffb2 ffffffd9 ffffff8a ffffffd9 ffffff84 ffffffd9 ffffff8a ffffffd8 ffffffa7 20 ffffffd8 ffffffa7 ffffffd9 ffffff84 ffffffd8 ffffffb1 ffffffd8 ffffffb3 ffffffd9 ffffff85 ffffffd9 ffffff8a REC = เวลาอินโดจีน Bytes: ffffffe0 ffffffb9 ffffff80 ffffffe0 ffffffb8 ffffffa7 ffffffe0 ffffffb8 ffffffa5 ffffffe0 ffffffb8 ffffffb2 ffffffe0 ffffffb8 ffffffad ffffffe0 ffffffb8 ffffffb4 ffffffe0 ffffffb8 ffffff99 ffffffe0 ffffffb9 ffffff82 ffffffe0 ffffffb8 ffffff94 ffffffe0 ffffffb8 ffffff88 ffffffe0 ffffffb8 ffffffb5 ffffffe0 ffffffb8 ffffff99 REC = heure normale d’Afrique de l’Ouest Bytes: 68 65 75 72 65 20 6e 6f 72 6d 61 6c 65 20 64 ffffffe2 ffffff80 ffffff99 41 66 72 69 71 75 65 20 64 65 20 6c ffffffe2 ffffff80 ffffff99 4f 75 65 73 74 REC = Центральная Америка летнее время Bytes: ffffffd0 ffffffa6 ffffffd0 ffffffb5 ffffffd0 ffffffbd ffffffd1 ffffff82 ffffffd1 ffffff80 ffffffd0 ffffffb0 ffffffd0 ffffffbb ffffffd1 ffffff8c ffffffd0 ffffffbd ffffffd0 ffffffb0 ffffffd1 ffffff8f 20 ffffffd0 ffffff90 ffffffd0 ffffffbc ffffffd0 ffffffb5 ffffffd1 ffffff80 ffffffd0 ffffffb8 ffffffd0 ffffffba ffffffd0 ffffffb0 20 20 ffffffd0 ffffffbb ffffffd0 ffffffb5 ffffffd1 ffffff82 ffffffd0 ffffffbd ffffffd0 ffffffb5 ffffffd0 ffffffb5 20 ffffffd0 ffffffb2 ffffffd1 ffffff80 ffffffd0 ffffffb5 ffffffd0 ffffffbc ffffffd1 ffffff8f REC = Ora de vară a Europei de Est Bytes: 4f 72 61 20 64 65 20 76 61 72 ffffffc4 ffffff83 20 61 20 45 75 72 6f 70 65 69 20 64 65 20 45 73 74
如您所见,数据在 UTF8 网页和旧的 Windows-1250 网页上也是可读的……但是其他任何东西都无法读取,我用谷歌搜索到死亡,并尝试了我在各个站点上找到的所有信息。是的,我看过 Rick James 的文档,但是 none 他的建议对我有用。
我已经尝试了所有我能想到的转换和转换的组合 最受欢迎的示例: SELECT CONVERT(CAST(CONVERT(txt USING latin1) AS BINARY) USING UTF8) AS res FROM test;
解码成功'Mitteleuropäische Normalzeit',但其他记录全部变为NULL。
我也试过java的解码器 byte[] utf8Bytes = rec.getString("txt").getBytes("ISO-8859-1"); 字符串结果 = 新字符串 (utf8Bytes, "UTF-8");
result 在网页上显示良好,但如果我将结果写入文件或将 result 发送到我的新数据库,它会变成又是垃圾
don’t go breaking my heart😛 😠Haha...... 🤤🤤🤤 Mitteleuropäische Normalzeit StÅ™edoevropský letnà Äas ä¸å›½æ ‡å‡†æ—¶é—´ Ð¦ÐµÐ½Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ Ð•Ð²Ñ€Ð¾Ð¿Ð° летнее Ð²Ñ€ÐµÐ¼Ñ Ð˜Ñ€ÐºÑƒÑ‚Ñк Ñтандартное Ð²Ñ€ÐµÐ¼Ñ heure d’été d’Europe centrale توقيت برازيليا الرسمي เวลาà¸à¸´à¸™à¹‚ดจีน heure normale d’Afrique de l’Ouest Ð¦ÐµÐ½Ñ‚Ñ€Ð°Ð»ÑŒÐ½Ð°Ñ Ðмерика летнее Ð²Ñ€ÐµÐ¼Ñ Ora de vară a Europei de Est
如你所见,我应该可以检索到数据,但还没有找到方法。
有人可以帮我解决这个问题吗?
记住,我只想将 unicode 数据以正确呈现的形式写入文件,或者将 unicode 数据以正确呈现的形式发送到我的新数据库。
鉴于现在没有人发布真正有用的解决方案,我想我会逆势而行。
如果您曾经遇到过这个问题并且想要使用 java8 提取双重编码数据并将其写入转储文件(如 csv 文件或 sql 文件),请尝试将其作为入门到您的项目...
// Create and fill a list of maps called recs
// Note: BaseMap is just my own class that extends Map, it has extensions like getString, getInt, etc
// I'm using column called "txt" to store some UTF8 test data
Path path = Paths.get("/my/file/name/here"); // <<< change this
try (BufferedWriter writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8)) {
for(int i=0; i<recs.size(); i++)
{
BaseMap rec = new BaseMap((Map)recs.get(i));
try {
byte[] doubleEncodedBytes = rec.getString("txt").getBytes("ISO-8859-1");
String decodedTxt = new String(doubleEncodedBytes, "UTF-8");
writer.append(decodedTxt);
writer.newLine();
// If you don't want to write to a file,
// you could instead execute on a connection to Database 2: INSERT INTO `schemaname`.`tablename` (columns...) VALUES (" decodedTxt ",....)
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
writer.flush();
} catch (IOException e) {
e.printStackTrace();
}
是的,这是一个缓慢的解决方案,但我阅读和尝试过的所有 MYSQL 解决方案都失败了,所以可以说这是我的“最终解决方案”
对于表情符号,您必须使用 MySQL 的 CHARACTER SET utf8mb4
。最好的方法是在连接期间建立它。第二好的是通过 SET NAMES utf8mb4;
.
SELECT UNHEX('646f6ee2809974207274f09f989b');
产量
don’t rt
适合你吗?但是——使用 SELECT HEX(col) ...
查看 col
中的内容;在您的代码显示十六进制之前 数据可能已被破坏 。
SELECT CONVERT(BINARY(CONVERT('d’Europe' USING latin1)) USING utf8mb4);
yields d’Europe mojibake to ut8 (or utf8mb4),
要进行更多调试,请执行
SELECT LENGTH(col), CHAR_LENGTH(col), col, HEX(col) FROM ...
如果是双重编码,我们可以从两个不同的长度来检测。
您显示了 CREATE TABLE
,但是加载数据的代码呢?以及倾倒它的代码?还是就地升级了?我的意思是,mojibake(等)可能是在升级期间发生的。
SELECT LENGTH('d’Europe'), CHAR_LENGTH('d’Europe'), 'd’Europe', HEX('d’Europe');
+----------------------+---------------------------+------------+----------------------+
| LENGTH('d’Europe') | CHAR_LENGTH('d’Europe') | d’Europe | HEX('d’Europe') |
+----------------------+---------------------------+------------+----------------------+
| 10 | 8 | d’Europe | 64E280994575726F7065 |
+----------------------+---------------------------+------------+----------------------+
“双重编码”的十六进制将是
64C3A2E282ACE284A24575726F7065
这显示了撤消它的最佳(?)方法:
SELECT CONVERT(BINARY(CONVERT(CONVERT(UNHEX('64C3A2E282ACE284A24575726F7065') USING utf8mb4) USING latin1)) USING utf8mb4);
+---------------------------------------------------------------------------------------------------------------------+
| CONVERT(BINARY(CONVERT(CONVERT(UNHEX('64C3A2E282ACE284A24575726F7065') USING utf8mb4) USING latin1)) USING utf8mb4) |
+---------------------------------------------------------------------------------------------------------------------+
| d’Europe |
+---------------------------------------------------------------------------------------------------------------------+
我浏览了您提供的其他行;他们似乎彼此一致。也就是说,使其中一个起作用的修复将适用于其他修复。 (一定要用表情符号检查一个。)
更多
这是一个硬汉。
Here's the first row from
SELECT HEX(col)
'646F6EC3A2C280C2997420676F20627265616B696E67206D79206865617274C3B0C29FC298C29B'
fordon’t go breaking my heart
让我从“正确的单引号”(’
) 开始,因为它似乎具有代表性。
Char UTF-8 If interpreted as latin1
’ E28099 ’
’ C3A2 C280 C299 You have this
’ C3A2 E282AC E284A2 Correct encoding
有些东西把 €
变成了 C280
。虽然反向工作“正确”,但它不是有效的 UTF-8 映射。我不知道那件事发生在哪里;我怀疑它是否在 MySQL 内。映射是在某些客户端完成的吗?
由此可见,MySQL不想使用C280
:
mysql> SELECT CAST(UNHEX('C3A2E282ACE284A2') AS char), CAST(UNHEX('C3A2C280C299') AS char);
+-----------------------------------------+-------------------------------------+
| CAST(UNHEX('C3A2E282ACE284A2') AS char) | CAST(UNHEX('C3A2C280C299') AS char) |
+-----------------------------------------+-------------------------------------+
| ’ | â |
+-----------------------------------------+-------------------------------------+
因此,我在 mysql 中没有看到 清理混乱的简单方法。但是,将 C280
转换为 80
.
如果你能以某种方式得到 string don’t
,然后将 string 送回 MySQL,加上一个 CONVERT 表达式,您也许可以修复它。
直到大约十年前,C280
是黑客用来将顽皮的东西绕过浏览器的诡计的一部分。但是浏览器变得聪明了。
如果您能找到 C280
的来源,请提交软件的安全错误报告。并停止使用它。
如果是在 MySQL 20 年前,您可能一直在使用 latin1
版本 4.0(没有其他字符集)或从 latin1 不正确转换的 4.1。或者可能仍在使用 latin1,但使用 UTF-8 字符只是横跨 2-3 个 latin1 字符。那时,错误的 ALTER
导致“双重编码”,但您可能没有注意到它,因为 INSERT
期间的 mangling 大部分在 SELECT
.
MySQL 5.5(2010 年底)引入了可以正确处理表情符号的 utf8mb4。
(与此同时,表情符号看起来就像是被破解的双重编码。)