在使用MotoJ2SDK进行J2ME应用程序的开发,经常会遇到中文的显示,存储,传输和编码的问题。首先要对388手机上开发Kjava程序时,对于中文的支持情况有所了解。
中文的支持有三种编码方式:
-ISO10646 and ISO8859_1 编码格式;
-UTF8编码格式;
-UNICODE
下面我们来看一下各种不同的编码方式有什么区别。在目录Text下有三个.txt文件,它们是分别用ANSI、UNICODE、UTF8方式编码的一段中文,内容为“摩托罗拉”。我们用UltraEdit分别打开三个文件,并且用HEX方式浏览,可以发现:
1. ANSI方式的16进制数据为
C4,A6,CD,D0,C2,DE,C0,AD,
其中每个中文占两个字节,并且每个字节都大于0xA0
2. UNICODE方式的16进制数据为
FF,FE,69,64,58,62,57,7F,C9,62
其中头两个字节“FF,FE”是固定的,表示该文本按照UNICODE编码,并且随后为每个中文占两个字节。
3. UTF8方式的16进制数据为
EF,BB,BF,E6,91,A9,E6,89,98,E7,BD,97,E6,8B,89
其中头3个字节“EF,BB,BF”是固定的,表示该文本按照UTF8编码,并且随后为每个中文占3个字节。
但是,仅了解这些,对于开发有关中文的应用程序还是不够的。如何能够保证正确的传输和存储中文才是根本目的。经常有些开发者会遇到中文无法存储,以及经过网络传输后得到的是乱码的问题。这是因为,J2ME中通常是不能对中文直接进行存储和传输的,要进行一定的转换。在Hopen的J2ME论坛中,有人提到这样的方法,即用以下两个函数把字符串先转成byte 数组后在写入数据库,在读出时也要先把byte数组转换成字符串,再进行显示等操作。
public static String byte2string(byte[] b,int offset,int len )
{
try{
ByteArrayInputStream bais = new ByteArrayInputStream(b,offset,len);
DataInputStream inputstream = new DataInputStream( bais );
return inputstream.readUTF();
}catch(IOException e){return null;}
}
public static byte[] string2byte(String s)
{
try{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream outputstream = new DataOutputStream(baos);
outputstream.writeUTF( s );
return baos.toByteArray();
}catch(IOException e){return null;}
}
经过试验,发现这是一种简单有效的方法,并且同样使用于UDP传输中文数据,即再传输前先转换成byte数组,接收后把byte数组再转换成字符串显示。在目录UTFTest中,我做了一个简单的实例。UTFSendTest.java中,通过TextBox输入一段要传输的中文,
String strText=mainScreen.getString();
然后定义一个byte数组,
byte[] bText = new byte[100];
然后把要发送的字符串利用前面的函数转换成byte数组,以便进行传输
bText = string2byte(strText);
int length = bText.length;
最后把转换后的byte数组数据发送到Server端,
dc = (DatagramConnection)Connector.open(destAddr);
Datagram dobject = dc.newDatagram(bText,length,destAddr);
dc.send(dobject);
在Server端的UTFTest.java中做一个逆过程即可,
dc = (DatagramConnection)Connector.open("datagram://:"+ port);
Datagram dobject;
byte[] bReceive = new byte[100];
dobject = dc.newDatagram(dc.getMaximumLength());
dc.receive(dobject);
bReceive = dobject.getData();
String strReceive = byte2string(bReceive,0,bReceive.length);
mainScreen.setString("已收到:"+strReceive);
现在似乎有了一个权宜之计,至少可以进行有关中文的操做了。但是有关中文的问题还没有彻底的说清楚。还有一个经常提到的问题,就是为什么不能象显示英文一样,用System.out.println(strReceive); 来显示一段中文字符串变量呢?这一点对于本来就不直观的调试界面来说也是十分必要的,开发者要经常通过这种方式来验证中文是否读取和传输正确。而实际上,我们看到的只是一串无奈的“???”。因此在MotoJ2SDK中,直接用
System.out.println ("中文");
或者是,
String testString = new String("中华人民共和国");
System.out.println(testString);
通常是不可行的。如何才能保证在模拟器界面和system.out中都能正确显示中文呢?下面我们以finalUDP目录中的UDPServer和UDPClient为例来分析一下。
如果采用上面的方法,利用函数byte2string()和 string2byte(),在进行传输之前都转换成Byte数组的格式,用UTF8的编码方式,可以得到正常的传输,Client端发出“发送摩托罗拉 ”,Server端收到后发出“返回摩托罗拉”,Client端接收到后显示在模拟器手机屏幕上,但是发送和接收中文字符串用 System.out.println()时,都是“???”。
下面对程序做如下改动,Server端受到“发送摩托罗拉”后,要发出“返回摩托罗拉”。但是这次不采用
static final String replyMsg=”返回摩托罗拉”;
byte[] bText = new byte[100];
bText = string2byte(replyMsg);
而是分别用bText =replyMsg.getBytes();
或
bText =replyMsg.getBytes("ISO10646");
或
bText =replyMsg.getBytes("ISO8859_1");
把待发字符串转换成字符数组,在Client端接收时不用以前的方式
dc.receive(dobject);
bReceive = dobject.getData();
receiveMsg = byte2string(bReceive,0,bReceive.length);
而是直接采用
dc.receive(receiveData);
receiveMsg = new String(receiveData.getData(),0,receiveData.getLength());
然后再把接收到字符串显示输出,为了细致分析接受到的字符数组每个元素的值,用如下方式读出并显示:
bReceive=receiveMsg.getBytes();
for(int i=0;i<bReceive.length;i++)
System.out.println("rcv"+i+":"+bReceive[i]);
经过试验,bText =replyMsg.getBytes();三种不同的参数情况,试验结果如下表所示:
参数情况 接收到的数组 元素情况 接收端模拟器 屏幕显示情况 接收端system.out 显示情况
Void 共12个bytes,每个汉字对应2个bytes,
并且值即为各自ANSI编码值,例如“返”对应0xB7,0xB5 乱码 正常
"ISO10646" Java.io.unSupportedEncodingException:ISO10646
"ISO8859_1" 共6个元素,每个均为0x3F, 即为“?”的ASCII码 ?????? ??????
通过以上试验我们发现
似乎中文字符串也可以用System.out.println()显示,不过要求特殊的编码方式。
从对字符串的是否可以正常操作的角度上看,可以把中文字符串分成两类
通过代码中直接赋值的中文字符串;
通过TextBox或者TextField等方式输入的中文字符串;
通过g.drawString()在模拟器手机屏幕上显示的中文字符串;
通过网络传输的中文字符串;
通过数据库读写的中文字符串;
通过System.out输出显示的字符串;
也就是说如果中文字符串在第一类情况中正常,则在第二类情况中就不正常。而对于同一类中的情况则是一致的。比如一个字符串可以在模拟器屏幕上正常显示,那么用sysem.out肯定时乱码。再比如用TextField输入一段字符串,在模拟器上也显示正常,那么肯定不能System.out显示。在上表中可以看出,接收字符串可以system.out正常输出,那么想把它直接在模拟器屏幕上显示也没戏。还有刚开始都用UTF8方式,采用函数 string2byte()和byte2string()时候模拟器屏幕得到正确显示,那么system.out就可能不行了。
现在问题,似乎就归结为如何在这两类直接进行过渡和切换,或者更直接一点,如何使得一个中文字符串在system.out和手机屏幕上都能得到正确的显示。也许通过程序上可以做到,当然也可能是MotoJ2SDK自身存在一些问题。