【原】UCS-2和UTF-8的互相转换
生活随笔
收集整理的這篇文章主要介紹了
【原】UCS-2和UTF-8的互相转换
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
為什么80%的碼農都做不了架構師?>>> ??
我們都知道對于UNICODE來說,UCS-2是內碼,而UTF-8則是它的實現方式。每一個字節都有8個位,而對于UTF-8來說,每一個字節的前兩位尤為重要,按照前兩位的不同,一共有四種排列組合:00xxxxxx,01xxxxxx,10xxxxxx,11xxxxxx。按照UTF-8標準,
(1)所有以0開始的字節,都與原來的ASCII碼兼容,也就是說,0xxxxxxx不需要額外轉換,就是我們平時用的ASCII碼。
(2)所有以10開始的字節,都不是每個UNICODE的第一個字節,都是緊跟著前一位。例如:10110101,這個字節不可以單獨解析,必須通過前一個字節來解析,如果前一個也是10開頭,就繼續前嗍。
(3)所有以11開始的字節,都表示是UNICODE的第一個字節,而且后面緊跟著若干個以10開頭的字節。如果是110xxxxx(就是最左邊的0的左邊有2個1),代表后面還有1個10xxxxxx;如果是1110xxxx(就是最左邊的0的左邊有3個1),代表后面還有2個10xxxxxx;以此類推,一直到1111110x。
具體的表格如下:
1字節 0xxxxxxx
2字節 110xxxxx 10xxxxxx
3字節 1110xxxx 10xxxxxx 10xxxxxx
4字節 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx
5字節 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
6字節 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx
(很明顯,以11開頭的,最左邊的0左邊有多少個1,那這個UCS的UTF-8的表示長度就有多少個字節)
上面是用6個字節,最多可以表示2 ^ 31個的字符,實際上,只有UCS-4才有這么多的字符,對于UCS-2,僅僅有2 ^ 16個字符,只需要三個字節就可以,也就是說,只需要用到下面的格式:
1字節 0xxxxxxx
2字節 110xxxxx 10xxxxxx
3字節 1110xxxx 10xxxxxx 10xxxxxx
大家可以試一下下面的program,來看看UTF-8的每個字節。
package com.ray.utf8;import java.io.UnsupportedEncodingException;public class UTF8Tester {private static String toBin(int n) {StringBuilder b = new StringBuilder();if (n < 0) n += 256;for (int i = 7; i >= 0; i--) {if (1 == ((n >> i) & 1)) {b.append('1');} else {b.append('0');}}return b.toString();}private static String HEX = "0123456789ABCDEF";private static String toHex(int n) {StringBuilder b = new StringBuilder();if (n < 0) n += 256;b.append(HEX.charAt(n >> 4));b.append(HEX.charAt(n & 0x0F));return b.toString();}private static void printUTF8(char ch) throws UnsupportedEncodingException {String unicode = toHex(ch >> 8) + toHex(ch & 0xFF);String unicodeBin = toBin(ch >> 8) + ' ' + toBin(ch & 0xFF);String s = "" + ch;byte[] b = s.getBytes("UTF-8");String hex = "";for (int i = 0; i < b.length; i++) {hex += toHex((int) b[i]);hex += " ";}String bin = "";for (int i = 0; i < b.length; i++) {bin += toBin((int) b[i]);bin += " ";}String sf = String.format("U+%s %s : %-8s : %s", unicode, unicodeBin, hex.trim(), bin.trim());System.out.println(sf);}public static void main(String[] args) throws Exception {printUTF8('\u002A');printUTF8('\u012A');printUTF8('\u012B');printUTF8('\u052C');printUTF8('\u013C');printUTF8('\uAA2A');printUTF8('\uFDFD');}}
輸出:
U+002A 00000000 00101010 : 2A?????? : 00101010
U+012A 00000001 00101010 : C4 AA??? : 11000100 10101010
U+012B 00000001 00101011 : C4 AB??? : 11000100 10101011
U+052C 00000101 00101100 : D4 AC??? : 11010100 10101100
U+013C 00000001 00111100 : C4 BC??? : 11000100 10111100
U+AA2A 10101010 00101010 : EA A8 AA : 11101010 10101000 10101010
U+FDFD 11111101 11111101 : EF B7 BD : 11101111 10110111 10111101
UCS-2和UTF-8的轉換,只涉及到位運算,不需要像GBK般需要查找代碼表,所以轉換效率很高。
先來說說UTF-8轉UCS-2:
(1)對于以0開始的字節,直接在前面部補一個0的字節湊成2個字節(即0xxxxxxx ==> 00000000 0xxxxxxxx);
(2)對于以110開始(110xxxxx)的字節,把后面緊跟著的一個10xxxxxx拿過來,首先在高位字節的左邊補5個零,然后把11個“x”放在右邊(即110xxxxx 10yyyyyy ==> 00000xxx xxyyyyyy);
(3)對于以1110開始(1110xxxx)的字節,把后面緊跟著的兩個10xxxxxx拿過來,數一下,一共有16個“x”,沒錯,就是把這16個“x”組成兩個字節(即1110xxxx 10yyyyyy 10zzzzzz ==> xxxxyyyy yyzzzzzz)。
在來說說UCS-2轉UTF-8:
(1)對于不大于0x007F(即00000000 01111111)的,直接把它轉成一個字節,變成ASCII;
(2)對于不大于0x07FF(即00000111 11111111)的,轉換成兩個字節,轉換的時候把右邊的11位分別放到110xxxxx 10yyyyyy里邊,即00000aaa bbbbbbbb ==> 110aaabb 10bbbbbb
(3)剩下的回轉換成三個字節,轉換的時候也是把16個位分別填寫到那三個字節里面,即aaaaaaaa bbbbbbbb ==> 1110aaaa 10aaaabb 10bbcccccc
下面是轉換的實現代碼:
package com.ray.utf8;import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException;public class Utf8Utils {private final static byte B_10000000 = 128 - 256;private final static byte B_11000000 = 192 - 256;private final static byte B_11100000 = 224 - 256;private final static byte B_11110000 = 240 - 256;private final static byte B_00011100 = 28;private final static byte B_00000011 = 3;private final static byte B_00111111 = 63;private final static byte B_00001111 = 15;private final static byte B_00111100 = 60;/** Convert from UTF8 bytes to UNICODE character */public static char[] toUCS2(byte[] utf8Bytes) {CharList charList = new CharList();byte b2 = 0, b3 = 0;int ub1 = 0, ub2 = 0;for (int i = 0; i < utf8Bytes.length; i++) {byte b = utf8Bytes[i];if (isNotHead(b)) {// start with 10xxxxxx, skip it.continue;} else if (b > 0) {// 1 byte, ASCIIcharList.add((char) b);} else if ((b & B_11110000) == B_11110000) {// UCS-4, here we skip itcontinue;} else if ((b & B_11100000) == B_11100000) {// 3 bytesb2 = utf8Bytes[i+1];if (!isNotHead(b2)) continue;i++;b3 = utf8Bytes[i+1];if (!isNotHead(b3)) continue;i++;ub1 = ((b & B_00001111) << 4) + ((b2 & B_00111100) >> 2);ub2 = ((b2 & B_00000011) << 6) + ((b3 & B_00111111));charList.add(makeChar(ub1, ub2));} else {// 2 bytesb2 = utf8Bytes[i+1];if (!isNotHead(b2)) continue;i++;ub1 = (b & B_00011100) >> 2;ub2 = ((b & B_00000011) << 6) + (b2 & B_00111111);charList.add(makeChar(ub1, ub2));}}return charList.toArray();}private static boolean isNotHead(byte b) {return (b & B_11000000) == B_10000000;}private static char makeChar(int b1, int b2) {return (char) ((b1 << 8) + b2);}public static byte[] fromUCS2(char[] ucs2Array) {ByteArrayOutputStream baos = new ByteArrayOutputStream();for (int i = 0; i < ucs2Array.length; i++) {char ch = ucs2Array[i];if (ch <= 0x007F) {baos.write(ch);} else if (ch <= 0x07FF) {int ub1 = ch >> 8;int ub2 = ch & 0xFF;int b1 = B_11000000 + (ub1 << 2) + (ub2 >> 6);int b2 = B_10000000 + (ub2 & B_00111111);baos.write(b1);baos.write(b2);} else {int ub1 = ch >> 8;int ub2 = ch & 0xFF;int b1 = B_11100000 + (ub1 >> 4);int b2 = B_10000000 + ((ub1 & B_00001111) << 2) + (ub2 >> 6);int b3 = B_10000000 + (ub2 & B_00111111);baos.write(b1);baos.write(b2);baos.write(b3);}}return baos.toByteArray();}private static class CharList {private char[] data = null;private int used = 0;public void add(char c) {if (data == null) {data = new char[16];} else if (used >= data.length) {char[] temp = new char[data.length * 2];System.arraycopy(data, 0, temp, 0, used);data = temp;}data[used++] = c;}public char[] toArray() {char[] chars = new char[used];System.arraycopy(data, 0, chars, 0, used);return chars;}}private static void assert1(String s) throws UnsupportedEncodingException {byte[] b = s.getBytes("utf-8");char[] c = toUCS2(b);if (!s.equals(new String(c))) {throw new RuntimeException("Can not pass assert1 for: " + s);}}private static void assert2(String s) throws UnsupportedEncodingException {byte[] b = s.getBytes("utf-8");byte[] b2 = fromUCS2(s.toCharArray());if (b.length == b2.length) {int i;for (i = 0; i < b.length; i++) {if (b[i] != b2[i]) {break;}}if (i == b.length) {return;}}throw new RuntimeException("Can not pass assert2 for: " + s);}public static void main(String[] args) throws Exception {assert1("test");assert1("中文測試");assert1("A中V文c測d試E");assert1("\u052CA\u052CBc測");assert2("test");assert2("中文測試");assert2("A中V文c測d試E");assert2("\u052CA\u052CBc測\u007F\u07FF");System.out.println("pass");}}
作者: liangguanhui?
聲明: 本文系JavaEye網站發布的原創文章,未經作者書面許可,嚴禁任何網站轉載本文,否則必將追究法律責任!
已有 3 人發表回復,猛擊->>這里<<-參與討論
JavaEye推薦
- 上海:天會皓聞誠聘CTO技術總監
- 上海:高薪誠聘Python開發人員
- 上海:天會皓聞誠聘資深Java架構師
- 成都:月薪5千到1萬招聘Java開發工程師
- 北京:手機之家網站誠聘PHP程序員
轉載于:https://my.oschina.net/soar/blog/2524
總結
以上是生活随笔為你收集整理的【原】UCS-2和UTF-8的互相转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 《OPhone应用开发权威指南》全面上市
- 下一篇: 《xUnit Test Patterns