发信人: lzzzl(lzzzl)
整理人: wenbobo(2003-08-23 11:01:40), 站内信件
|
unicode<->utf8我写过,但是是根据自己的需要写的,是JAVA版的,核心代码其实不长,其它辅助代码写得比较多。如果只用于特定目的,可以大大简化
/**
* Title: <p>
* Description: <p>
* Copyright: Copyright (c) <p>
* 2003.02.24<br>
* Company: philosoft<p>
* @author zzl([email protected])
* @version 1.0
*/
package com.philo.general;
/**
* UTF-8与Unicode编码的数据的转换器<br>
* 主要目前是给数据编码以便于传输<br>
* 例如编码后的数据只是十六制数据,数据体与协议可分离<br>
* 此类的参数限于文本消息,特别是中英文混合的文本。如果全是中文,那么使用UTF-8编码后,将比原来的Unicode编码多出50%的长度,反而不好<br>
* 另外对文件的转换其实无所谓编码,一律看作二进制数据字节进行十六进制编码即可,其转换过程以及数据压缩、检验等就不是UTF8这个类所该做的事情了<br>
* 提供两种编码的数据的相互转换(目前仅通过Unicode的ISO8859-1和GB2312两个子集的测试)<br>
* 目前为简单起见,超时不处理转换异常<br>
* 参考文档《无废话XML》及本程序的相关开发文档
*/
public class UTF8
{
private final static char DEFAULT_UNKNOWN_CHAR ='?'; // 默认替字符
private static String unknowChars ="" + UTF8.DEFAULT_UNKNOWN_CHAR; // 替代内容
/**
* 设置转换时遇到的丢失或混乱字符的替代内容
* @param p 替代内容
*/
public static void setUnknowChars(String p)
{
if(p==null){ // 简单容错,无效值好象不能代表什么特殊含义
UTF8.unknowChars ="";
}else{
UTF8.unknowChars =p;
}
}
//-------------------------------Unicode2UTF8-------------------------------
/**
* Unicode转换为UTF-8编码的数据<br>
* 为简单起见,此方法不处理异常
* @param unicode Unicode编码的数据
* @return UTF-8编码的数据,以16进制串的格式返回
* @see toOneUTF8Hex(char)
*/
public static String Unicode2UTF8Hex(String unicode)
{
String s =null;
s ="";
int L =unicode.length();
// 逐个字符转换
for(int i=0;i<L;i++){
s +=UTF8.toOneUTF8Hex(unicode.charAt(i));
}
return s;
}
/**
* 一个Unicode字符转换成一个UTF-8字符的核心算法<br>
* 为简单起见,此方法不处理异常
* @param c Unicode编码的字符
* @pram UTF-8编码的字符,以16进制串的格式返回,不定长,但总是2,4或6之一
*/
public static String toOneUTF8Hex(char c)
{
String s =null;
s ="";
int i =(int)c;
if(i<=0x007E){
s +=UTF8.hex(c); // L=2
}else if(i<=0x07FF){
s +=UTF8.hex((byte)(0xC0 + ((i>>6) & 0x1F))); // L=4,先移位后运算可简化一些
s +=UTF8.hex((byte)(0x80 + (i & 0x3F)));
}else{
s +=UTF8.hex((byte)(0xE0 + ((i>>12) & 0x0F))); //L=6
s +=UTF8.hex((byte)(0x80 + ((i>>6) & 0x3F)));
s +=UTF8.hex((byte)(0x80 + ((i & 0x3F))));
}
return s;
}
/**
* 取16进制串
* @param b 数值(0∽255)
* @return 2个16进制字符组成的串
*/
public static String hex(byte b)
{
String s =null;
// 转换成非负数
int i =(int)b;
i +=256;
i %=256;
// 转换成2个16进制字符
s =UTF8.hex(i>>4) + UTF8.hex(i%16);
return s;
}
/**
* 取16进制串(方法重载)
* @see hex(byte)
*/
public static String hex(char c){
return UTF8.hex((byte)c);
}
/**
* 取16进制串
* @param n 数值(0∽15)
* @return 1个16进制字符组成的串
*/
public static String hex(int n)
{
String s ="";
// 简单处理非法值
n %=16;
if(n<10){
s +=n;
}else{
s +=(char)((int)'A' + (n-10));
}
return s;
}
//-------------------------------UTF82Unicode-------------------------------
/**
* UTF-8编码的数据(16进制表示)转换为Unicode编码的数据<br>
* 为简单起见,此方法不处理异常
* @param utf8hex UTF8编码(16进制表示)的数据,长度应为偶数
* @return Unicode编码的字符串
*/
public static String UTF8Hex2Unicode(String utf8hex)
{
String s =null;
s ="";
int L =utf8hex.length();
// 逐个“小块”小心转换
for(int i=0;i<L-1;) //1-
{
byte b1 =(byte)UTF8.valueOf(utf8hex.substring(i,i+2)); // bi表示第i个字节,1∽3个字节将组合成一个UTF8字符。16进制字符总是成对出现,不会有孤立的情况(下同)
if(UTF8.isByteOfOneByteChar(b1)){ //2-
s +=(char)UTF8.valueOf(utf8hex.substring(i,i+2));
i +=2;
}else if(UTF8.isHeadOfTwoBytesChar(b1))
{
// 试图读取2个bytes(4个16进制字符)。
if(i+4<=L){
}else // 当前字符表面好但内部已经变“坏”的恶劣情况不会出现在此分支
{
s +=UTF8.unknowChars;
break; // 处理完毕,不必处理下标移动问题
}
// 尽量准确地断字,但显然太乱了也无能为力
byte b2 =(byte)UTF8.valueOf(utf8hex.substring(i+2,i+4));
if(UTF8.isComingByteOfMultBytesChar(b2)){ // 正是期望中的1个后继byte
s +=(char)(((b1 & 0x1F)<<6) + (b2 & 0x3F));
i +=4;
}else if(UTF8.isHeadOfUTF8Char(b2)){ // 如果下一byte是另一个Unicode的头部(当然假设这个头不是“假”的啦)也好
s +=UTF8.unknowChars;
i +=2;
}else{ // 否则再贪“吃”一口
s +=UTF8.unknowChars;
i +=4;
}
}else if(UTF8.isHeadOfThreeBytesChar(b1))
{
// 试图读取3个bytes(6个16进制字符)
if(i+6<=L){ //3-
}else if(i+4<=L)
{
// 处理当前字符表面好但内部已经变“坏”的恶劣情况
byte b2 =(byte)UTF8.valueOf(utf8hex.substring(i+2,i+4));
if(UTF8.isByteOfOneByteChar(b2)) // 后继有“人”,不过只能是个“小人”,即一个byte的ASCII字符
{
s +=UTF8.unknowChars;
i +=2;
continue; // 和“小人”打交道的事留待下一循环再说,先了结当前问题再说
}else{
s +=UTF8.unknowChars;
break; // 处理完毕,不必处理下标移动问题
}
}else{ // 当前字符表面好但内部已经变“坏”的恶劣情况不会出现在此分支
s +=UTF8.unknowChars; // 处理完毕,不必处理下标移动问题
break;
} //-3
// 尽量准确地断字,但显然太乱了也无能为力
byte b2 =(byte)UTF8.valueOf(utf8hex.substring(i+2,i+4));
byte b3 =(byte)UTF8.valueOf(utf8hex.substring(i+4,i+6));
if(UTF8.isComingByteOfMultBytesChar(b2) && UTF8.isComingByteOfMultBytesChar(b3)){ // 正是期望中的2个后继bytes
s +=(char)(((b1 & 0x0F)<<12) + ((b2 & 0x3F)<<6) + (b3 & 0x3F));
i +=6;
}else if(UTF8.isHeadOfUTF8Char(b2)){ // 如果下一byte是另一个Unicode的头部(当然假设这个头不是“假”的啦)也好
s +=UTF8.unknowChars;
i +=2;
}else if(UTF8.isHeadOfUTF8Char(b3)){ // 如果再下一byte是另一个Unicode的头部(当然假设这个头不是“假”的啦)也好
s +=UTF8.unknowChars;
i +=4;
}else{ // 否则再贪“吃”两口
s +=UTF8.unknowChars;
i +=6;
}
}else{ // 乱得没有“头绪”,只好快刀斩乱麻了,错误的东西怎么说都是垃圾,处理掉就算不错了
s +=UTF8.unknowChars;
i +=2; // 和垃圾打交道的事当然也留待下一循环再说啦
} //-2
} //-1
return s;
}
/**
* 是否新UTF-8字符开始
* @param 组成UTF-8字符的字节
* @return 结果。true表示是,false表示否
*/
public static boolean isHeadOfUTF8Char(byte b)
{
boolean ok =false;
ok = ok || UTF8.isByteOfOneByteChar(b);
ok = ok || UTF8.isByteOfOneByteChar(b);
ok = ok || UTF8.isHeadOfThreeBytesChar(b);
return ok;
}
/**
* 是否单字节UTF-8字符
* @param 组成UTF-8字符的字节
* @return 结果。true表示是,false表示否
*/
public static boolean isByteOfOneByteChar(byte b){
return (b & 0x80)==0x00;
}
/**
* 是否双字节UTF-8字符首字节
* @param 组成UTF-8字符的字节
* @return 结果。true表示是,false表示否
*/
public static boolean isHeadOfTwoBytesChar(byte b){
return (b & 0xE0)==0xC0;
}
/**
* 是否三字节UTF-8字符首字节
* @param 组成UTF-8字符的字节
* @return 结果。true表示是,false表示否
*/
public static boolean isHeadOfThreeBytesChar(byte b){
return (b & 0xF0)==0xE0;
}
/**
* 是否多字节UTF-8字符后继字节
* @param 组成UTF-8字符的字节
* @return 结果。true表示是,false表示否
*/
public static boolean isComingByteOfMultBytesChar(byte b){
return (b & 0xC0)==0x80;
}
/**
* UTF8Hex2Unicode()中所有组合的完全测试<br>
* 根据Case树得到的解析单个字符时可能的所有20种组合
*/
private static void completedTest()
{
String[] list ={
"41",
"C2B1",
"C241",
"C2C2",
"C2E4",
"C2F0",
"C2",
"E4B8AD",
"E4B841",
"E4B8C2",
"E4B8E4",
"E4B8F0",
"E4B8",
"E441",
"E4C2",
"E4E4",
"E4F0",
"E4",
"F0",
""
};
int L =list.length;
for(int i=0;i<L;i++){
Debugger.debug("" + (i+1) + ":\tUTF8Hex2Unicode(\"" + list[i] + "\")=" + UTF8.UTF8Hex2Unicode(list[i]));
}
}
/**
* 16进制串转10进制数值<br>
* 为简单起见,此方法不处理异常
* @param hex 16进制字符组成的串
* @return 转换得到的数值
*/
public static int valueOf(String hex)
{
int k =0;
int L =hex.length();
for(int i=0;i<L;i++)
{
char c =hex.toUpperCase().charAt(i); // 统一大小写可简化后面的代码
if(c>='0' && c<='9'){
k =(k<<4) + ((int)c) -((int)'0');
}else{
k =(k<<4) + 10 + ((int)c) -((int)'A'); // 如果传入的数据不合法,就难说是n进制还是-m进制了
}
}
return k;
}
/**
* 16进制串转10进制数值(方法重载)
* @see valueOf(String)
*/
public static int valueOf(char c){
return UTF8.valueOf("" + c);
}
/**
* 调试入口
* @param argv 命令行参数(一维字符数组)
*/
public static void main(String[] argv)
{
Exception ee =null;
try
{
if(true)
{
Debugger.debug("hex(\"A\")=" + UTF8.hex('A'));
Debugger.debug("valueOf(\"41\")=" + UTF8.valueOf("41"));
Debugger.debug("valueOf('2'))=" + UTF8.valueOf('2'));
Debugger.debug("valueOf('B'))=" + UTF8.valueOf('B'));
Debugger.debug("unicode(\"A\")=" + hex('A'));
Debugger.debug("unicode(\"±\")=" + hex('±'));
Debugger.debug("unicode(\"中\")=" + hex('中'));
Debugger.debug("1&2=" + (1&2));
}else{
}
String s =UTF8.Unicode2UTF8Hex(argv[0]);
Debugger.debug("Unicode2UTF8Hex(\"" + argv[0] + "\")=" + s);
// 加点干扰(重点考查GB2312区)
s +="E4B8AD"; // 正常
s +="E4E4AD"; // 结束得早
s +="E4B8E4";
s +="E441AD";
s +="E4B841";
s +="80" + "E4B8AD"; // 无头绪
Debugger.debug("UTF8Hex2Unicode(\"" + s + "\")=" + UTF8.UTF8Hex2Unicode(s));
UTF8.completedTest();
}catch(ArrayIndexOutOfBoundsException aioobe){
ee =aioobe;
Debugger.debug("参数不足!");
}catch(Exception e){
ee =e;
}finally
{
if(ee==null){
}else{
Debugger.debug(ee);
Debugger.debug("用法:...<要转换的字符串>");
}
}
}
}
---- 不想计较得失,却总在计较得失 |
|