最近在做 Android 版本的落格输入法,在导入码表的时候我犯了难。因为落格输入法的码表是支持 utf8 和 gb18030 两种编码格式的,甚至我自己内置的码表也是混用这两种格式的。在 Swift 或者 Python 中,如果你使用错误的编码去解码文本,就会收到报错。利用这个办法,我可以轻松实现两种编码的检测——先用 utf8 解码,报错了就再试试 gb18030. 简单方便,足够我用。
可是这个情况在 Kotlin 中不灵了!在 Java 中,如果你使用错误编码解码文本,它只会给你一坨乱码!这在其他语言中通常都需要明确强制解码才能实现……但在 Java 中是默认的,而且没有办法让它报错。
1 2 3 4 5 6 7 8 9 |
try { val assetName = "" val inputStream = context.assets.open(assetName) reader = InputStreamReader(inputStream, Charsets.UTF_8) // 任何编码的文本都不会触发报错 } catch (e: IOException) { // means file not in the asset, so we try load it from the file system } catch (e: Exception) { return importFalse(context, "码表编码格式错误,请确定编码为 uft-8 不要有 BOM!") } |
这就很难办了,经过查询,我找到了一个号称能检测各种编码的第三方库:https://github.com/albfernandez/juniversalchardet 但很遗憾,它并不能成功检测 gb18030.
最后,中文的问题果然还得用中文来搜索,http://www.meilongkui.com/archives/473 我找到了这么一篇文章,据说文中的调用方法是可以触发报错的。我按文中的方案先读取一小段内容,然后进行转换…………成功了!但也有个小问题,由于读取的buffer长度是截断的,所以有可能导致即使是 utf8 的文本,也不能成功用 utf8 解出来,因为你可能恰好 buffer 到了一个字符编码的中间。
最终,我的解决方案是一次性读取全部内容,这样就不会出错啦。
当然,代价就是需要一次性将所有内容读进内存。对我来说问题不大,因为输入法码表通常并不是很大。如果有必要,其实也可以对 buffer 的大小或者位置进行一点偏移然后再试一次之类的。
最终,代码是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
val buffer: ByteArray = inputStream.readAllBytes() inputStream.close() var content: String? = null try { content = Charsets.UTF_8.newDecoder().decode(ByteBuffer.wrap(buffer)).toString() } catch (_: Exception) { // do nothing try next one } try { content = Charsets.GB18030.newDecoder().decode(ByteBuffer.wrap(buffer)).toString() } catch (_: Exception) { // do nothing try next one } if (content == null) { return importFalse(context, "码表编码格式错误,请确定编码为 uft-8 不要有 BOM!") } |
本文由 落格博客 原创撰写:落格博客 » Kotlin/Android 检测文本编码
转载请保留出处和原文链接:https://www.logcg.com/archives/3868.html