基于android获得SIM卡三种格式的UCS2编码

之前的工作涉及到SIM卡的UCS2编码,需要将字符用81格式编码,在网上找了许多资料,结合自己的研究,现和大家一起分享

本文主要介绍如何获得字符的80,81,82格式编码,代码在andoid平台上测试,成功存储联系人信息并成功解码。由于有些东西是自己研究所得,难免会有错误,如有不实的地方,欢迎批评指正。

解码文件及方法: frameworks/base/telephony/java/com/android/internal/telephony/uicc/IccUtil.java的adnStringFieldToString()方法。

什么是UCS2编码?

UCS2(Unicode Character Set)是一种字符编码方式,2代表一个字符编码后为2个字节。

我们在SIM卡上存储联系人时就是使用UCS2编码。

 

SIM卡上的UCS2编码主要有三种格式:80,81,82

 

下面说明每种格式的编码方式:

‘80’格式:

1.第一个字节为0x80

2.之后的字节为UCS字符,每个字符占用2个字节。(在android平台上用UTF-16BE编码)。

例:东方不败

编码后的字符为:4e1c 65b9 4e0d 8d25

用80格式表示是:80 4e1c 65b9 4e0d 8d25

‘81’格式:

1. 第一个字节为0x81

2.第二个字节为字符串长度

3.第三个字节为基址,为一个UCS码的第15位到第8位(0xxx xxxx x000 000)

4.第四个字节以后为81格式的UCS码

  1. 如果第8位是0(xxxx xxxx 0xxx xxxx),则此字符的81格式编码就用第7到1位表示(0000 0000 0xxx xxxx),第8位补0;
  2. 如果第8位是1(xxxx xxxx 1xxx xxxx),则此字符的81格式编码为UCS编码减去基址左移7位,第8位补1。

例:5Жик

编码后的字符为:0035 0416 0438 043a

用81格式表示是:81 04 08 35 96 b8 ba

下面我们以这个例子来看一下基址以及字符编码的获得:

取得第一个字节不是’00’的字符编码:

0416 = 0000 0100 0001 0110

0438 = 0000 0100 0011 1000

043a = 0000 0100 0011 1010

所以基址为0000 1000,也就是08

然后用基址算出这4个字符的81编码:

0035 = 0000 0000 0011 0101,第8位为0,所以它的81编码为0011 0101,也就是53

0416 = 0000 0100 0001 0110,基址左移7位为0000 0100 0011 1010 = 0400,减去基址以后为16,也就是0001 0110,然后最高位补1,为1001 0110,即96

后面的以此类推,为b8,ba

从这里可以看出,81格式只能在第15位到第8位相同的情况下才能进行。也就是说编码的范围只由后7位决定,而且是连续的,也就是2的7次方,即128个字符。所以中文并不适合用81格式来编码,因为两个汉字之间的UCS编码很可能大于128。

‘82’格式:

1.第一个字节为0x82

2.第二个字节为字符串长度

3.第三和第四个字节为基址,基址可以为UCS码中第一个字节不为0的最小值。

4.第四个字节以后为82格式的UCS码

  1. 如果第8位是0(xxxx xxxx 0xxx xxxx),则此字符的82格式编码就用第7到1位表示(0000 0000 0xxx xxxx),第8位补0;
  2. 如果第8位是1(xxxx xxxx 1xxx xxxx),则此字符的82格式编码为UCS编码减去基址,第8位补1。

例:a不乎了

编码后的字符为:0061 4e0d 4e4e 4e86

用82格式表示是:82 04 4e0d 61 80 c1 f9

下面我们以这个例子来看一下基址以及字符编码的获得:

选取UCS码第一个字节不为0的最小值,此处的四个字符第一个字节都不为0,所以选择这四个字符的最小值作为基址,也就是4e0d

然后用基址算出这4个字符的82编码:

0061 = 0000 0000 0110 0001,第8位为0,所以它的82编码为61

4e0d = 0100 1110 0000 1101,减去基址4e0d等于0,最高位补1为1000 0000,即80

4e4e = 0100 1110 0100 1110,减去基址等于41,最高位补1为1100 0001,即c1

剩下的4e86以此类推。

82格式和81有同样的限制,同样也只能表示128个字符,而且也要求连续。

那我们在什么情况下选择80,81还是82格式呢?

1.如果可能的话,尽量选择81格式,因为同样大小的字节用81格式可以表示最多的字符。如果有14个字节可以存储,那么使用81格式可以存储11个字符(3+N)。但是81格式最多只能表示128个字符。

2.选择82格式,14个字节可以存储10个字符(4+N),同样最多只能表示连续的128个字符。

3.选择80格式,14个字节可以存储6个字符(1+2N),但是80格式可以表示的范围广,可以从0000到FFFF。所以在大多数情况下汉字只能用80格式。

 

下面是一个用java写的例子以及测试程序:

 1 /**
 2      * 函数会优先使用81和82格式编码,如果不满足编码条件,选择80格式编码
 3      *
 4      * @param src 源UCS2字节数组
 5      * @param srcOff 源字节数组起始位置
 6      * @param srcLen 源字节数组长度
 7      * @param destOff 目标数组的起始位置
 8      * @param dest 目标数组
 9      * @return 目标数组
10      */
11     public static byte[] ucs2ToAlphaField(byte[] src, int srcOff, int srcLen,
12             int destOff, byte dest[]) {
13         int i;
14         int min = 0x7FFF;
15         int max = 0;
16         int temp;
17         int outOff;
18         // 当源数组的长度大于2时才能使用81或者82格式
19         if (srcLen > 2) {
20             // 取得源数组的最小值与最大值
21             for (i = 0; i < srcLen; i += 2) {
22                 //每个字符的第1个字节不为0
23                 if (src[srcOff + i] != 0) {
24                     temp = (int) (((src[srcOff + i] << 8) & 0xFF00) | (src[srcOff
25                             + i + 1] & 0xFF));
26                     // 因为在81格式中基址左移7位后最高位要补0,所以8000到FFFF不能使用81
27                     if (temp < 0) {
28                         max = min + 130;
29                         break;
30                     }
31                     if (min > temp) {
32                         min = temp;
33                     }
34                     if (max < temp) {
35                         max = temp;
36                     }
37                 }
38             }
39         }
40         // 如果所有字符的UCS码都在连续的128的范围内,那么第1个字节都相同
41         if ((max - min) < 129) {
42             // 如果第15位到第8位相同,就可以使用81格式编码
43             if ((byte) (min & 0x80) == (byte) (max & 0x80)) {
44                 // 算出81格式的长度,初始化目标数组
45                 dest = new byte[srcLen / 2 + 3];
46                 // 设置目标数组的第2个字节为长度
47                 dest[destOff + 1] = (byte) (srcLen / 2);
48                 // 设置第1个字节为81
49                 dest[destOff] = (byte) 0x81;
50                 // 基址为第15位到第8位,
51                 min = (int) (min & 0x7F80);
52                 dest[destOff + 2] = (byte) ((min >> 7) & 0xFF);
53                 outOff = destOff + 3;
54             }
55             // 第8个字节不一样(一个为0,一个为1),那么就使用82格式
56             else {
57                 // 算出82格式的长度,初始化目标数组
58                 dest = new byte[srcLen / 2 + 4];
59                 // 设置目标数组的第2个字节为长度
60                 dest[destOff + 1] = (byte) (srcLen / 2);
61                 // 设置第1个字节为82
62                 dest[destOff] = (byte) 0x82;
63                 // 基址为源数组最小值(第1个字节为0的除外)
64                 dest[destOff + 2] = (byte) ((min >> 8) & 0xFF);
65                 dest[destOff + 3] = (byte) (min & 0xFF);
66                 outOff = destOff + 4;
67             }
68 
69             for (i = 0; i < srcLen; i += 2) {
70                 // 如果第1个字节为0,则取7到1位,最高位补0
71                 if (src[srcOff + i] == 0) {
72                     dest[outOff] = (byte) (src[srcOff + i + 1] & 0x7F);
73                 }
74                 // 如果第1个字节不为0,则用UCS码减去基址,最高位补1
75                 else {
76                     temp = (int) ((((src[srcOff + i] << 8) & 0xFF00) | (src[srcOff
77                             + i + 1] & 0xFF)) - min);
78                     dest[outOff] = (byte) (temp | 0x80);
79                 }
80                 outOff++;
81             }
82             // 返回目标数组
83             return dest;
84         }
85 
86         // 不满足81和82的格式要求,使用80格式编码
87         dest = new byte[srcLen + 1];
88         dest[destOff] = (byte) 0x80;
89         System.arraycopy(src, 0, dest, 1, srcLen);
90         // 返回80格式目标数组
91         return dest;
92     }
View Code
 1 public static void main(String args[]){
 2         String src = "5Жик";
 3         try {
 4             byte[] dest = null;
 5             byte[] srcByte = src.getBytes("UTF-16BE");
 6             dest = ucs2ToAlphaField(srcByte, 0, srcByte.length, 0, dest);
 7             for (int i = 0; i < srcByte.length; i++) {
 8                 System.out.print(Integer.toHexString(srcByte[i] & 0xFF) + " ");
 9             }
10             System.out.println();
11             for (int i = 0; i < dest.length; i++) {
12                 System.out.print(Integer.toHexString(dest[i] & 0xFF) + " ");
13             }
14         } catch (UnsupportedEncodingException e) {
15             e.printStackTrace();
16         }
17     }
View Code

 

posted @ 2013-06-12 22:57  残阳破晓  阅读(977)  评论(0编辑  收藏  举报