android java unicode 之间的关系

背景
使用正则表达式寻找emoji字符,进行过滤
1.通过
http://apps.timwhitlock.info/emoji/tables/unicode 确定emoji 的字符码点范围在 \u1F600-\u1F6FF 之间
需要查看unicode码点和UTF-8 UTF-16 UTF-32的童鞋可以参考这篇文章
http://www.ruanyifeng.com/blog/2014/12/unicode.html

2.使用正则开始匹配
string 就是那个含有emoji unicode码点的字符串
if (Pattern.compile("[\u1F600-\u1F6FF]"),
Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE ).matcher(string).find()) {
return "有emoji字符无法创建 ucs-4";
}

匹配不成功
但是使用
if (Pattern.compile("[\ud83c\udc00-\ud83c\udfff]|[\ud83d\udc00-\ud83d\udfff]|[\u2600-\u27ff]",
Pattern.UNICODE_CASE | Pattern.CASE_INSENSITIVE ).matcher(text).find()) {
return res.getString(R.string.move_content_invalid_emoji);
}

却可以匹配成功,很是费解
于是使用了必杀技,打印字节流进行数据分析
发现
UTF-8 \xf0\x9f\x98\x89\xf0\x9f\x98\x89\xf0\x9f\x98\x89\xf0\x9f\x98\x89\xf0\x9f\x98\x89
UTF-16BE \xd8\x3d\xde\x09\xd8\x3d\xde\x09\xd8\x3d\xde\x09\xd8\x3d\xde\x09\xd8\x3d\xde\x09
UTF-16LE \x3d\xd8\x09\xde\x3d\xd8\x09\xde\x3d\xd8\x09\xde\x3d\xd8\x09\xde\x3d\xd8\x09\xde
UTF-32BE \x00\x01\xf6\x09\x00\x01\xf6\x09\x00\x01\xf6\x09\x00\x01\xf6\x09\x00\x01\xf6\x09
UTF-32LE \x09\xf6\x01\x00\x09\xf6\x01\x00\x09\xf6\x01\x00\x09\xf6\x01\x00\x09\xf6\x01\x00

一串emoji字符使用不同的方式获取字节流后是如上的情况
我们使用的\u1F600 显然属于UTF-32BE的unicode实现方式
\ud83c\udc00 显然属于 UTF-16BE的unicode实现方式
然后,然后就费解了
开始怀疑 android 到底是不是UTF-8编码,为什么运行时使用UTF-16BE却是正确的呢?
开始怀疑Pattern 是如何使用我传进去的正则表达式的
尝试使用 new String(text.getBytes(“UTF-8”), “UTF-32BE”); 转化字符串编码(这是严重错误的)
然后还是不行,再然后
然后晕了。

通过查看这里http://www.2cto.com/kf/201303/195387.html 的文章,和同事交流明白了3个概念
1. Java采用的编码
2. JVM平台默认字符集
3. 外部资源的编码。

Java的class文件采用utf8的编码方式,
JVM运行时采用utf16
Java的字符串是unicode编码的
总结上面的意思也就是说 String对象一定是UTF16编码的,不管是从class文件还是从外部资源来的

也就是可以理解为new String(text.getBytes(“UTF-8”), “UTF-32BE”) 这个是以UTF-8的方式获取text的字节流然后使用UTF-32BE的方式分析该字节流,最后生成UTF-16方式的字符串

下面重申一个例子
字符串编码迷思:

Java代码
new String(input.getBytes("ISO-8859-1"), "GB18030")
上面这段代码代表什么?有人会说: “把input字符串从ISO-8859-1编码方式转换成GB18030编码方式”。
如果这种说法正确,那么又如何解释我们刚提到的java字符串都采用unicode编码呢?

这种说法不仅是欠妥的,而且是大错特错的,让我们一一来分析,其实事实是这样的:我们本应该用
GB18030的编码来读取数据并解码成字符串,但结果却采用了ISO-8859-1的编码,导致生成一个错误的字
符串。要恢复,就要先把字符串恢复成原始字节数组,然后通过正确的编码GB18030再次解码成字符串(即把以GB18030编码的数据转成unicode的字符串)。注意,字符串永远都是unicode编码的。
但编码转换并不是负负得正那么简单,这里我们之所以可以正确地转换回来,是因为 ISO8859-1 是单字节编码,所以每个字节被按照原样 转换为 String ,也就是说,虽然这是一个错误的转换,但编码没有改变,所以我们仍然有机会把编码转换回来!
dalvik jvm 使用UTF-16貌似还有一个地方可以证明
http://developer.android.com/reference/java/util/regex/Pattern.html
Escape sequences
\Quote the following metacharacter (so \. matches a literal .).
\Q Quote all following metacharacters until \E.
\E Stop quoting metacharacters (started by \Q).
\\ A literal backslash.
\uhhhh The Unicode character U+hhhh (in hex).
\xhh The Unicode character U+00hh (in hex).
\cx The ASCII control character ^x (so \cH would be ^H, U+0008).
\a The ASCII bell character (U+0007).
\e The ASCII ESC character (U+001b).
\f The ASCII form feed character (U+000c).
\n The ASCII newline character (U+000a).
\r The ASCII carriage return character (U+000d).
\t The ASCII tab character (U+0009).

转义字符这段写的显然不是 UTF-32的字符
\uhhhh 但是这却是UTF-16可以表示的 而且使用两个\uhhhh可以表示unicode 所有的码点
UTF-8 最起码前面有个0 以示我是UTF-8方式的编码

几个沉思

  1. android dalvik jvm用的是什么UTF-16BE 还是UTF-16LE
  2. jvm 使用UTF-16是JVM规范还是各种虚拟机自己的标准
  3. android class 使用UTF-8 运行到虚拟机的时候使用UTF-16 运行时做转化?
  4. android log 输出时使用的是 UTF-8 JVM岂不是也要做一次转换? 怎么考虑的 效率何在?

欢迎大家批评指正

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。