之前的工作涉及到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碼
例:
編碼後的字元為:0035 0416 0438 043a
用81格式表示是:81 04 08 35 96 b8 ba
下面我們以這個例子來看一下基址以及字元編碼的獲得:
取得第一個位元組不是’00’的字元編碼:
0416 = 0001 0110
0438 = 0011 1000
043a = 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碼
例:
編碼後的字元為: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寫的例子以及測試程式:
[] ucs2ToAlphaField([] src, srcOff, destOff, min = 0x7FFF max = 0 (srcLen > 2 (i = 0; i < srcLen; i += 2 (src[srcOff + i] != 0 temp = () (((src[srcOff + i] << 8) & 0xFF00) | + i + 1] & 0xFF (temp < 0 max = min + 130 (min > min = (max < max = ((max - min) < 129 (() (min & 0x80) == () (max & 0x80 dest = [srcLen / 2 + 3 dest[destOff + 1] = () (srcLen / 2 dest[destOff] = () 0x81 min = () (min & 0x7F80 dest[destOff + 2] = () ((min >> 7) & 0xFF outOff = destOff + 3 dest = [srcLen / 2 + 4 dest[destOff + 1] = () (srcLen / 2 dest[destOff] = () 0x82 dest[destOff + 2] = () ((min >> 8) & 0xFF dest[destOff + 3] = () (min & 0xFF outOff = destOff + 4 (i = 0; i < srcLen; i += 2 (src[srcOff + i] == 0 dest[outOff] = () (src[srcOff + i + 1] & 0x7F temp = () ((((src[srcOff + i] << 8) & 0xFF00) | + i + 1] & 0xFF)) - dest[outOff] = () (temp | 0x80 outOff++ dest = [srcLen + 1 dest[destOff] = () 0x80 System.arraycopy(src, 0, dest, 1 }View Code String src = "5Жик" [] dest = [] srcByte = src.getBytes("UTF-16BE" dest = ucs2ToAlphaField(srcByte, 0, srcByte.length, 0 ( i = 0; i < srcByte.length; i++ System.out.print(Integer.toHexString(srcByte[i] & 0xFF) + " " ( i = 0; i < dest.length; i++ System.out.print(Integer.toHexString(dest[i] & 0xFF) + " " } }View Code