- 浏览: 45909 次
- 性别:
- 来自: 叶柏寿
-
最近访客 更多访客>>
文章分类
最新评论
-
top_kevin:
fgd
JVM加载class文件的原理机制 -
0640110146:
按照这个方法解决,不过貌似每个包下面都有一个svn目录,每个 ...
svn文件被锁不能提交解决办法
1. 简介
本文介绍JavaME中文编码的相关问题,这个问题一度是互联网上的开发者们讨论的热门话题。本文整理和综合了网上众多相关内容,尽可能的为开发者提供一个全面、系统的认识。
文中的代码仅用来说明原理,可能很不完整,缺乏变量定义或者返回值,请谅解。部分代码直接来源于网上的其他资料。
感谢众多开发者在中文编码问题上做出的努力与探索。总结中有什么问题的话,欢迎大家指正:)
2. 术语介绍
2.1 ASCII
基于罗马字母表的一套电子计算器编码系统,是单字节的编码方式,每个ASCII字符占用1个字节(8bits),所以ASCII编码最多可以表示256个字符。它是美国信息交换标准委员会(American Standards Committee for Information Interchange)的缩写, 为美国英语通信所设计。
显然ASCII编码用来表示英文字母和字符是足够了的,但是对于中文和日文等众多的文字来说,是远远不够的。
跟ASCII类似的编码还有ISO8859-1。
2.2 UNICODE
双字节编码方式,它为每种语言中的每个字符设定了统一并且唯一的二进制编码,以满足跨语言、跨平台进行文本基准转换、处理的要求。UNICODE支持欧洲、非洲、中东、亚洲(包括统一标准的东亚像形汉字和韩国像形文字)的文字。
UNICODE又可以分为“高位在前”和“低位在前”的两种格式,这和CPU的处理方式有点关系。这一点可以通过BOM(Byte Order Mark)来标示,若采用 “低位在前”方式编码,BOM 会表示为 0xFF 0xFE,而在 Unicode 的定义中是不存在 U+FFFE 这个字符的。若采用高位在前方式编码,BOM 会表示为 0xFE 0xFF,而 U+FEFF 刚好是在 Unicode 中的有效字符,代表的是一个不占空间的 space 符号,所以即使没被解释为 BOM,也不会对阅览者产生错误的信息。
但UINICODE也带来一些问题,当美国人看见自己每天最常用的字符需要用两倍的空间来保存时,自然会觉得这是一种浪费,他们一定会说:看看那堆0。于是新的编码方式又诞生了。
2.3 UTF-8
UTF的全称是UCS Transformation Format,即把Unicode转做某种格式的意思。目前存在的UTF格式有:UTF-7, UTF-7.5, UTF-8, UTF-16, 以及 UTF-32,本文讨论UTF-8格式。
UTF-8是UNICODE的一种变长字符编码,理论上使用1~6个字节来编码UNICODE。
虽然理论上UTF-8最多为6个字节,但是,由于双字节的Unicode最大为0XFFFF,所以双字节的Unicode转为UTF-8后最长为3个字节。
下列字节串用来表示一个字符。 用到哪个串取决于该字符在 Unicode 中的序号。
U-00000000 - U-0000007F: 0xxxxxxx U-00000080 - U-000007FF: 110xxxxx 10xxxxxx U-00000800 - U-0000FFFF: 1110xxxx 10xxxxxx 10xxxxxx U-00010000 - U-001FFFFF: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx U-00200000 - U-03FFFFFF: 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx U-04000000 - U-7FFFFFFF: 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
上表中的xxx为Unicode编码的二进制数据。例如:“中”字,Unicode编码为4E2D。
Unicode: 4E 2D 01001110 00101101 UTF-8: E4 B8 AD 11100100 10111000 10101101
对于英文来说,UTF-8跟ISO8859-1一样节约;但显然中文等字符将为UTF-8付出更多。
2.4 GB2312
GB2312又称国标码,由国家标准总局发布,1981年5月1日实施,通行于大陆。新加坡等地也使用此编码。它是一个简化字的编码规范,当然也包括其他的符号、字母、日文假名等,共7445个图形字符,其中汉字占6763个。我们平时说6768个汉字,实际上里边有5个编码为空白,所以总共有6763个汉字。
GB2312规定“对任意一个图形字符都采用两个字节表示,每个字节均采用七位编码表示”,习惯上称第一个字节为“高字节”,第二个字节为“低字节”。GB2312中汉字的编码范围为,第一字节0xB0-0xF7(对应十进制为176-247),第二个字节0xA0-0xFE(对应十进制为160-254)。
GB2312具有很多扩展,其中GBK是微软对GB2312的扩展,GB18030则是2000年发布的国家标准,是到目前为止最新的国标汉字编码。
3.JavaME中的字符编解码
3.1 编解码方法
说到JavaME中的字符编码问题,自然要从String类入手,在String类中我们可以找到字符编解码的相关方法:
1. 解码:
public String(byte[] bytes, String enc) throws UnsupportedEncodingException
2. 编码:
public byte[] getBytes(String enc) throws UnsupportedEncodingException
举例来说,“诺基亚”三个汉字的GB2312编码为C5 B5 BB F9 D1 C7。
代码段一,解码试验:
byte[] codes = {0XC5, 0XB5, 0XBB, 0XF9, 0XD1, 0XC7}; String string = new String(codes, “gb2312”); testForm.append(string);
得到的结果为:诺基亚
代码段二,编码试验:
byte[] codes = “诺基亚”.getBytes(“gb2312”); for (int i = 0, t = codes.length; i < t; i ++) { String hexByte = Integer.getHexString(codes[i]); if (hexByte.length() > 2) { hexByte = hexByte.subString(hexByte.length() - 2); } testForm.append(“0X”+ hexByte.subString + “, ”); }
得到的结果为:0XC5, 0XB5, 0XBB, 0XF9, 0XD1, 0XC7,
3.2 检查设备的编码支持情况
一个最直接的获取编码支持的方法是使用System.getProperty(“microedition.encoding”),可以得到设备的默认的字符编码,以NOKIA设备为例,得到的属性值为ISO8859-1。
然而,通过这个方法的意义并不大。首先,它只能获取到一个编码格式,而一般设备都会支持很多种编码规范;其次,这个属性的数值与虚拟机的实现有很大关系,同样以NOKIA S40v2为例,不论设备的目标市场使用什么语言,这个属性统一为ISO8859-1[参考资料5],显然ISO8859-1对于中文来说是毫无意义的。
再回过头来看看上一节中的两个方法,它们都会抛出一个UnsupportedEncodingException。利用这一点,我们可以自己来做一个设备支持编码规范情况的测试。
boolean isEncodingSupported (String encoding) { try { "诺基亚".getBytes(encoding); return true; } catch (UnsupportedEncodingException uee) { return false; } }
这里需要提醒的是,对于同一个编码格式来说,可能会有很多种不同的名称,例如Unicode在NOKIA的设备上用的是ucs-2,再例如utf-8来说,utf-8、utf8和utf_8都会有可能。对于这一点,CLDC的规范中并没有给出严格的定义。所以在实际测试的过程中需要充分考虑到这个情况。
3.3 readUTF() 和 writeUTF(String)
CLDC中还有两个方法跟字符编码有关系:DataInputStream中的readUTF()和DataOutputStream中的writeUTF(String)。根据两个方法的Java Doc,writeUTF(String)首先会向输出流中写入字符串编码成UTF8格式后的byte数组长度(2个字节),然后再将这个UTF8的byte数组写入。而readUTF()则是先从输入流中读取2个字节,组成一个short数值,在从输入流中读出这个数值长度的byte数据,再将这个byte数组解码成字符串。详细说明请参考DataInputStream和DataOutputStream的Java Document。
4.具体问题的解决
上一部分中介绍了CLDC平台上所有跟字符编解码相关的API。了解了这些内容以后,就可以结合具体的情况来考虑如何解决中文编码的问题了。
首先,中文编码问题中最常见的情况就是乱码,那么乱码是如何产生的呢?无非是以下几种情况:
- 指定的编码格式与数据实际的编码格式不符,造成数据被编码成指定的替代符号或被解释成与源字符完全不同的字符;
- 指定的编码格式正确,数据不完整或被篡改,造成数据无法在字符集中找到与其对应的编码;
所以,解决中文编码问题的几个基本思路是:
- 存储或传输前,确保数据被正确编码;
- 确保存储、读取和传输的过程完整、正确;
- 解码时,使用与编码时同样的编码格式;
- 确认编码格式是否被设备支持;
4.1 RMS存储的中文问题
这个问题完全可以使用readUTF和writeUTF来解决。
用UTF8编码向RecordStore中写入中文:
String content = "中文字符";
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeUTF(content);
byte[] bytes = bos.toByteArray();
rs.addRecord(bytes, 0, bytes.length);
从RecordStore中读出UTF8编码的中文:
byte utfBytes[] = rs.getRecord(dbid); DataInputStream dis=new DataInputStream(new ByteArrayInputStream(utfBytes)); String content = dis.readUTF();
当然,使用UTF8对于中文来说意味着更大的存储空间,所以也可以使用类似Unicode和GB2312等2字节的编码。在此之前,请通过3.2中描述的方式检测编码是否被支持。
用GB2312编码向RecordStore中写入中文:
String content = "中文字符";
byte[] bytes = content.getBytes("gb2312");
rs.addRecord(bytes, 0, bytes.length);
从RecordStore中读出GB2312编码的中文:
byte gb2312Bytes[] = rs.getRecord(dbid); String content = new String(gb2312Bytes, "gb2312");
4.2 从Resource文件中读取中文
大概可以分为两种情况,一是这个文件遵循某种特定的格式,例如RPG游戏的关卡文件,其中包含地图、事件和对话等数据,文件由特定的程序生成,为自有格式。这种情况基本上可以使用与上一小节中同样的方式来解决。关键是,存储和读取的过程遵从同样的数据和编码格式。
另一种情况是txt文件,可能通过一些文本编辑工具生成。Txt文件中常见的编码格式有Unicode、UTF8、Unicode Big Endian等。在我们读取txt文件之前,最先要确认的就是这个txt文件所使用的编码格式。
以UTF8为例:
in = getClass().getResourceAsStream(filename); in.read(word_utf); in.close(); string =new String(word_utf,"UTF-8");
对于Unicode来说,这里引用了参考1中的一段代码,这段代码实际上是在处理“低位在前”的Unicode:
public static String unicodeBytesToString(byte abyte0[], int i) { StringBuffer stringbuffer = new StringBuffer(""); for(int j = 0; j < i; ) { int k = abyte0[j++]; //注意在这个地方进行了码制的转换 if(k < 0) k += 256; int l = abyte0[j++]; if(l < 0) l += 256; char c = (char)(k + (l << 8));//把高位和低位数组装起来 stringbuffer.append(c); } }
对于高位在前的Unicode,可以使用和UTF8类似的方式。请注意,这里的"ucs-2"是针对诺基亚设备的,其他厂商设备可能与此不同,请查阅相关文档或自行测试。
in = getClass().getResourceAsStream(filename); in.read(word_ unicode); in.close(); string =new String(word_unicode, "ucs-2");
实际上经过我在NOKIA设备上的测试,对于低位在前的Unicode,也可以使用这个方式。前提是需要确保在数据的最前端添加低位在前的BOM(0XFFFE)。
继续使用“诺基亚”为例,高位在前和低位在前的的Unicode编码分别为:
nokiaBE = {(byte)0x8b, (byte)0xfa, (byte)0x57, (byte)0xfa, (byte)0x4e, (byte)0x9a,} nokiaLE = {(byte)0xfa, (byte)0x8b, (byte)0xfa, (byte)0x57, (byte)0x9a, (byte)0x4e,};
new String(nokiaBE, "ucs-2")的结果是“诺基亚”,而new String(nokiaLE, "ucs-2")的结果则是乱码。然后,我们对nokiaLE做出修改:
nokiaLE = {(byte)0xff, (byte)0xfe, (byte)0xfa, (byte)0x8b, (byte)0xfa, (byte)0x57, (byte)0x9a, (byte)0x4e,};
修改后,再次执行new String(nokiaLE, "ucs-2",则得到的结果也是“诺基亚”。
BTW,对于Resouce文件来说,虽然使用Unicode编码存储中文看起来像是比UTF-8要更节约,但是当Resource资源被打成Jar包时,压缩后的文件大小可能很接近。
4.3通过网络读取中文
和前面描述的一样,避免乱码的关键同样是保证编解码使用同样的格式,也就是客户端与服务器段保持同样的字符编码。
对于socket连接来说,传输的内容可以使用自定义的数据格式,所以处理的方式完全和前面两节是相同的,甚至可以向http学习,在数据的开头约定字符编码。
对于http连接,在http数据头中已经约定了编码格式,使用这个编码格式解码即可。另外一个很常见的问题,就是从xml文件中解析中文。对于这一点,在kxml2中已经有了很好的解决方案。
org.kxml2.io.KXmlParser.setInput(InputStream is, String _enc)
你可以通过_enc指定一个编码格式,如果_enc为null,则Parser会根据数据的特性自动尝试各种编码格式。由于kxml为开源项目,如果这里的处理方式需要调整,你也可以自己动手去完善它的功能。
在kxml2中也增加了对wml文件的解析,有兴趣的可以研究一下,这一部分我没有作过尝试。
4.4 JAD中的中文
JAD文件如果需要使用中文,则需要使用UTF8格式。关于如何在JAD中写入UTF8数据,可以有很多方法,可以使用一些Ultral Editor之类的文本工具,或使用一些JAD/MENIFEST生成工具。你甚至可以自己编写一个JAD/MENIFEST的生成工具,对于JavaSE平台来说,这并不是一件太难的工作,你还可以给这个工具赋予更多的功能,例如自动填写vendor和version之类的字段。
特别说明,对于NOKIA Series 40v2等设备来说,通过OTA下载Jad/Jar时,需要在服务器端为JAD添加MIME type时标明encoding为UTF8,否则即使你的JAD确实使用了UTF8格式,安装之后仍然会是乱码。
4.5 Unicode和UTF8的互转
在网上有一些Unicode和UTF8互相转换的代码,是直接通过编码格式去实现的,从前文叙述的Unicode和UTF8之间的关系来看,Unicode和UTF8之间很容易互相转换,计算量也不会太大。这里就不在把这些代码贴出来了,有兴趣的可以自己找一下,或者自己写一个。
但是,实际上Unicode和UTF8这两个编码格式基本是所有设备都会支持的,直接通过String类的编解码方法互转就可以了。
4.6设备不支持的编码格式
如果需要使用设备不支持的编码格式,那么必然将付出一些额外的代价。例如,如果设备不支持GB2312,则你可以有如下的几个选项中选择其一:
- 在你的程序中包含一个GB2312的字符对应表;(也许GB2312你还可以接收,但是GB18030要怎么办呢?)
- 做一个代理服务器,将GB2312转解码成可以被设备支持的编码;
- 放弃GB2312,选择其他编码;
发表评论
-
一台服务器部属两个JBOSS方案
2010-01-09 17:43 1044解决办法: 修改 \jboss-4.0.4.GA\server ... -
对比一下
2010-01-07 19:29 729/** *分段批量处理 *看看什么是垃圾代码 */ ... -
DeploymentUtil.isSourceFolder(Unknown Source) 问题解决
2009-11-23 16:47 14301.安装后,部署工程hmsi时,出现error的一个错误对话框 ... -
servlet-api-2.4.jar - jar not loaded 问题解决
2009-11-23 16:45 2478信息: validateJarFile(G:\workspac ... -
JVM加载class文件的原理机制
2009-11-19 10:36 2085Java 语言是一种具有动 ... -
web工程识别
2009-11-16 09:17 781修改工程目录下的.project文件 替换成下面 <?x ... -
java存储过程调用 MSSQL
2009-11-13 10:43 1116存储过程: --call MyProc(?,?,?)al ... -
有谁来买我的火柴
2009-11-01 21:37 689今天,在桂林繁華的中山路上,蹲在桃花江橋正式掛牌賣火柴了 有 ... -
关于static变量,成员变量和局部变量
2005-06-10 13:58 843我找到一个很经典的例子,看明白了就可以了解他们各自的特性了. ... -
NIO介绍
2007-04-20 14:03 720前言: 可悲的是我参考的这些技术资料是在02~04年的,那时 ... -
Java程序员认证模拟题及详细分析
2007-05-11 13:44 680Java程序员认证模拟题及详细分析 ------------ ... -
使用AXIS开发webservice(转载)
2008-03-26 15:37 596今天是感恩节,差点又要在公司加班了。好在Web Servic ... -
webservice 基于对象的传输
2008-03-26 15:38 996由于最近公司采用Web Services技术做企业应用集成( ... -
在axis中实现附件上传
2008-03-29 16:04 1212说明:最后两句非常关键!!! --------------- ... -
java时间定义及转换
2009-02-21 13:47 775时间定义及转换: 2008-04-10 11:12:12 & ... -
java Collection
2009-07-23 21:03 715Java面试中,最常被人问到的几个问题: 1. java.ut ...
相关推荐
3. 处理多语言支持:对于支持中文的应用,开发者可以检查多语言切换下的字体显示是否正常,防止乱码问题。 4. 验证文本对齐和布局:在不同字体下,文本的长度和宽度可能会有所变化,因此需要确保文本布局不受影响。 ...
在Java ME(J2ME)环境中,汉字编码的相互转换是一项关键任务,因为不同的设备和平台可能使用不同的字符编码标准。本文将详细讲解如何在J2ME中进行UTF-8、Unicode、GB2312以及GBK编码之间的转换,帮助开发者解决跨...
- Java语言编码规范(简体中文) - Servlet Essentials(英文) - JDK SDK Docs(各版本英文) - JavaTM 2 Platform, Standard Edition API Specification(各版本英文) - JavaTM 2 Platform Enterprise Edition ...
Java ME(微型版),则针对嵌入式和移动设备开发,如手机应用软件。 安装Java开发工具包(JDK)是学习Java的第一步。JDK包含了编译、运行Java程序所需的所有工具。教程详细指导了如何根据操作系统下载并安装JDK,...
在深入探讨《Scalable IO in Java》的中文版内容之前,首先需要了解Java中的I/O模型发展历程及其在服务器编程中的重要性。在Java中,I/O处理经历了从传统的BIO(阻塞I/O),到NIO(非阻塞I/O),再到AIO(异步I/O)...
Java平台根据不同的应用场景,可以分为三个主要版本:JavaSE、JavaEE和JavaME。JavaSE(Java Standard Edition)是Java的标准版,主要面向桌面应用和数据库开发;JavaEE(Java Enterprise Edition)为企业版,提供了...
- Java 语言编码规范(Java Code Conventions)(简体中文) - Java 版模式速查手册(英文) - Servlet Essentials(英文) - JDK SDK Docs(各版本英文) - JavaTM 2 Platform, Standard Edition API ...
Eclipse ME 是一款专为移动设备开发设计的集成开发环境(IDE),它是Eclipse IDE的扩展,主要用于Java ME(Micro Edition)应用的开发。这款工具提供了丰富的功能,帮助开发者高效地构建、测试和调试J2ME应用程序。...
在处理中文字符时,如果出现乱码问题,可能需要检查文件的编码格式,确保其为ANSI或UTF-8等支持中文的格式。 总的来说,Java基础知识涉及计算机语言的分类、Java语言的发展历史、JDK的使用和配置,以及Java程序的...
在Java开发方面,Eclipse与Java Development Kit(JDK)紧密集成,支持Java SE、Java EE和Java ME的项目开发。它内置了Tomcat、Jetty等应用服务器,方便进行Web应用的运行和调试。Eclipse还支持Maven和Gradle等构建...
native2ascii命令用于编码转换,将中文字符的Unicode编码转换为ASCII编码。javap命令用于将编译后的class文件反编译为Java字节码。jdb命令是Java的调试工具。jps命令用于查看JVM进程信息。keytool命令用于生成...
### Java手机程序设计入门6 —— Motorola ...此外,还提到了支持中文显示的方法以及LWT的基本介绍,这些都是开发Java ME移动应用时不可或缺的知识点。希望读者能够在实践中进一步熟悉这些技术,提高自己的开发能力。
**21天学通JAVA2(中文版第三版)**是一本旨在帮助初学者快速入门Java编程的经典教程。本书通过逐步引导的方式,帮助读者建立起对Java语言及其开发环境的全面认识。 #### 二、Java平台介绍 Java语言因其强大的跨...
- JavaME(Java Micro Edition)面向嵌入式和移动设备开发,最常用在手机应用软件的开发中。 2. 安装工具包 - 要开始Java开发,首先需要安装Java开发工具包(JDK)。JDK是Java的核心,包含了Java运行环境(JRE)...
- **GB2312**:中文编码标准之一,主要支持简体中文。 - **GBK**:兼容GB2312,支持更多汉字。 - **UTF-8**:通用的多字节编码格式,广泛用于Web页面和文件系统。 - **ANSI**:随操作系统而异的本地编码方式。 ...
同时,还需要编写汉字编码和显示算法,以支持汉字的显示。程序调试包括逻辑错误检查、性能优化和功能验证。 7 结论 基于单片机的LED显示器设计不仅提升了显示系统的灵活性,而且降低了成本,易于扩展。结合软硬件...
- **平台**:Java平台主要分为三个部分:Java SE(标准版)、Java EE(企业版)和Java ME(微型版)。 5. **JDK、JRE、JVM的作用及关系** - **JDK(Java Development Kit)**:包含Java运行环境、Java工具和Java...