`
frenchmay
  • 浏览: 231405 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Unicode文件中文编码解码的实现

阅读更多

<!---->

近期开发一个处理WMA 文件的公用类库,发现最头疼的问题不是WMA编码格式的问题而是Unicode这种文件格式的问题.经过近两天的研究,终于解决了这一问题,并且在今天下午基本完成了WMA文件解析处理类库的开发.利用余下的一点时间偷偷将Unicode相关的内容整理一下,希望我的老板不要发现这件事情.o(∩_∩)o...哈哈

字节流的字符编码:
字符编码把字符转换成数字存储到计算机中,按 ASCii 将字母映射为整数。
把数字从计算机转换成相应的字符的过程称为解码。
乱码的根源在于编解码方式不统一。在世界上任何一种编码方式中都会向上兼容 ASCII 码。所以英文没有乱码。
编码方式的分类:
ASCII
(数字、英文) :1 个字符占一个字节(所有的编码集都兼容 ASCII
ISO8859-1
(欧洲): 1 个字符占一个字节
GB-2312/GBK
1 个字符占两个字节。 GB 代表国家标准。
GBK
是在 GB 2312 上增加的一类新的编码方式,也是现在最常用的汉字编码方式。
Unicode: 1
个字符占两个字节(网络传输速度慢)
UTF-8
:变长字节,对于英文一个字节,对于汉字两个或三个字节。
原则:保证编解码方式的统一,才能不至于出现错误。

<!----> <!---->

UTF 的字节序和 Byte Order Mark

UTF-8 以字节为编码单元,没有字节序的问题。 UTF-16 以两个字节为编码单元,在解释一个 UTF-16 文本前,首先要弄清楚每个编码单元的字节序。例如收到一个 Unicode 编码是 594E Unicode 编码是 4E59 。如果我们收到 UTF-16 字节流 “594E” ,那么这是 还是

<!----> <!---->

Unicode 规范中推荐的标记字节顺序的方法是 Byte Order Mark 。而是 Byte Order Mark 。在 UCS 编码中有一个叫做 "ZERO WIDTH NO-BREAK SPACE" 的字符,它的编码是 FEFF 。而 FFFE UCS 中是不存在的字符,所以不应该出现在实际传输中。 UCS 规范建议我们在传输字节流前,先传输字符 "ZERO WIDTH NO-BREAK SPACE"

这样如果接收者收到 FEFF ,就表明这个字节流是 Big-Endian 的;如果收到 FFFE ,就表明这个字节流是 Little-Endian 的。

<!----> <!---->

UTF-8 不需要 Byte Order Mark 来表明字节顺序,但可以用 Byte Order Mark 来表明编码方式。字符 "ZERO WIDTH NO-BREAK SPACE" UTF-8 编码是 EF BB BF 。所以如果接收者收到以 EF BB BF 开头的字节流,就知道这是 UTF-8 编码了。

如同样是字符 "A" ﹐在以下几种格式中的存储形式分别是﹕
UTF-16 big-endian : 00 41
UTF-16 little-endian : 41 00
UTF-32 big-endian : 00 00 00 41
UTF-32 little-endian : 41 00 00 00

<!----> <!---->

因此在一段字节流开始时﹐如果接收到以下字节﹐则分别表明了该文本文件的编码。
UTF-8: EF BB BF
UTF-16 : FF FE
UTF-16 big-endian: FE FF
UTF-32 little-endian: FF FE 00 00
UTF-32 big-endian: 00 00 FE FF
而如果不是以这个开头﹐那程序则会以 ANSI, 也就是系统默认编码读取。

<!----> <!---->

ANSI 文件格式的字节流﹕
BA-BA-41
10111010 10111010 01000001
Unicode
文件格式的字节流﹕
FF-FE-49-6C-41-00
11111111 11111110 01001001 01101100 01000001 00000000
Unicode-big-endian
文件格式的字节流﹕
FE-FF-6C-49-00-41
11111110 11111111 01101100 01001001 00000000 01000001
utf-8
文件格式的字节流﹕
EF-BB-BF-E6-B1-89-41
11101111 10111011 10111111 11100110 10110001 10001001 01000001

<!----> <!---->

<!----> <!---->

解码

<!----> <!---->

处理 WMA 文件时 , 例如读到一首歌曲的 title 名称为 ”2046 沧海一声笑

使用 byte[] 直接在文件中读到的

[50, 0, 48, 0, 52, 0, 54, 0, -89, 108, 119, 109, 0, 78, -16, 88, 17, 123]

50, 0 对应 2

48, 0 对应 0

52, 0 对应 4

54, 0 对应 6

-89, 108 对应

119, 109 对应

0, 78 对应

-16, 88 对应

17, 123 对应

<!----> <!---->

readUnicodeByte2Hex 的代码实现Unicode字节到16进制码的转换

 

public static String readUnicodeByte2Hex(byte[] temp) throws IOException {

        StringWriter sw = new StringWriter();
       
        for (int index = 0; index < temp.length; index++) {
           
            if (temp[index] > 0xf && temp[index] <= 0xff) {
               
                sw.write(Integer.toHexString(temp[index]));
               
            } else if (temp[index] >= 0x0 && temp[index] <= 0xf) {// 对于只有1位的16进制数前边补“0”
               
                sw.write("0" + Integer.toHexString(temp[index]));
               
            } else {
                // 对于int<0的位转化为16进制的特殊处理,因为Java没有Unsigned
                // int,所以这个int可能为负数
                sw.write(Integer.toHexString(temp[index]).substring(6));
            }
        }

        return sw.toString();
    }

<!----> <!---->

转化为 16 进制

3200 3000 3400 3600 a76c 776d 004e f058 117b

  2     0     4     6                   

<!----> <!---->

<!----> <!---->

System.out.println((char) Integer.parseInt("a76c", 16));

输出 : 乱码 ?.

于是 考虑到可能是 Byte Order Mark 的原因 , 尝试将沧字的 16 进制码的高低位进行交换

System.out.println((char) Integer.parseInt("6c a7", 16));

输出 :  沧

 

decodeHexToChinese的代码实现

 

public static String decodeHexToChinese(String hex) {
        StringBuffer sb=new StringBuffer("");
        for(int i=0;i<hex.length();i=i+4){
            String temp=hex.substring(i, i+4);
            if(temp.equalsIgnoreCase("0000"))
                break;
            String temp2=""+temp.charAt(2)+temp.charAt(3)+temp.charAt(0)+temp.charAt(1);
            char t=(char) Integer.parseInt(temp2, 16);
            sb.append(t);
        }
        return sb.toString();
    }

<!----> <!---->

编码

<!----> <!---->

例如尝试将 ”2046 沧海一声笑 写入 WMA 文件

将字符串转换为 hex 的字符串 , 然后转化为 byte 数组

writeNew2Buff

 

    public static void writeNew2Buff(String title,byte[] titleBuff ) throws NumberFormatException,
        IOException {

        String titelHex=toHexString(title);

        int t=0;

//将16进制字符串转化为byte格式
        for(int i=0;i<titelHex.length();i=i+2){
   
            titleBuff[t++]=(byte) Integer.parseInt(titelHex.substring(i, i + 2), 16);
   
        }
    }

 

toHexString 将字符串转化为符合Unicode格式规范的16进制字符串

 

 public static String toHexString(String s){
        StringBuffer sb=new StringBuffer("");
        byte[] temp;   
        try {
            temp = s.getBytes("Unicode");

            for(int i=2;i<temp.length;i+=2){  

                if((temp[i]&0xff)<16){
                    sb.append("0"+Integer.toHexString(temp[i]&0xff));
                }else{
                    sb.append(Integer.toHexString(temp[i]&0xff));
                }
                if((temp[i+1]&0xff)<16){
                    sb.append("0"+Integer.toHexString(temp[i+1]&0xff));
                }else{
                    sb.append(Integer.toHexString(temp[i+1]&0xff));
                }       

            }
            return sb.toString();
        } catch (UnsupportedEncodingException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return s;           
    }

 

编码为

[50, 0, 48, 0, 52, 0, 54, 0, -89, 108, 119, 109, 0, 78, -16, 88, 17, 123]

<!----> <!---->

对于写入数字的特殊处理

由于数字存在精度的问题 , 因此 unicode 的文件中数字的精度有 2byte,4byte,8byte 的差别

因此写入数字的时候要考虑位数的差别问题 . 而且空白位要使用 0 进行补足 .

下面给出int转化为2byte和4byte的技术实现.

<!----> 

    public static byte[] intTo2Bytes(int s) {
        byte[] buf = new byte[2];

        int pos;
        for (pos = 0; pos < 2; pos++) {
            buf[pos] = (byte) (s & 0xff);
            s >>= 8;
            if (s == 0)
                break;

        }
        return buf;
    }

    public static byte[] intTo4Bytes(int i) {
        byte[] arrB = new byte[4];
        arrB[3] = (byte) (i >> 24);
        arrB[2] = (byte) (i >> 16);
        arrB[1] = (byte) (i >> 8);
        arrB[0] = (byte) i;
        return arrB;
    }

今天早上发现temp = s.getBytes("Unicode");在winxp和win2k上面得到的结果是不同的.
winxp和win2k上面对字符串转Unicode的Byte Order Mark 是不同的,不过可以通过
byte数组的前两位进行判断,通过判断ffef还是efff来辨别是big-endian或是litte-endian.toHexString方法代码更新如下
public static String toHexString(String s){
StringBuffer sb=new StringBuffer("");
byte[] temp;
try {
temp = s.getBytes("Unicode");

if(-1==temp[0]&&-2==temp[1]){
for(int i=2;i<temp.length;i+=2){
if((temp[i]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i]&0xff));
}else{
sb.append(Integer.toHexString(temp[i]&0xff));
}
if((temp[i+1]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i+1]&0xff));
}else{
sb.append(Integer.toHexString(temp[i+1]&0xff));
}

}
}else
if(-2==temp[0]&&-1==temp[1]){
for(int i=2;i<temp.length;i+=2){
if((temp[i+1]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i+1]&0xff));
}else{
sb.append(Integer.toHexString(temp[i+1]&0xff));
}
if((temp[i]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i]&0xff));
}else{
sb.append(Integer.toHexString(temp[i]&0xff));
}
}
}
return sb.toString();
} catch (UnsupportedEncodingException e) {

e.printStackTrace();
}
return s;
}
分享到:
评论
2 楼 hippo 2009-06-13  
能提供源码就更好,感激不尽!
1 楼 frenchmay 2008-07-30  
今天早上发现temp = s.getBytes("Unicode");在winxp和win2k上面得到的结果是不同的.
winxp和win2k上面对字符串转Unicode的Byte Order Mark 是不同的,不过可以通过
byte数组的前两位进行判断,通过判断ffef还是efff来辨别是big-endian或是litte-endian.toHexString方法代码更新如下
public static String toHexString(String s){
    StringBuffer sb=new StringBuffer("");
byte[] temp;
try {
temp = s.getBytes("Unicode");

if(-1==temp[0]&&-2==temp[1]){
for(int i=2;i<temp.length;i+=2){ 
if((temp[i]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i]&0xff));
}else{
sb.append(Integer.toHexString(temp[i]&0xff));
}
if((temp[i+1]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i+1]&0xff));
}else{
sb.append(Integer.toHexString(temp[i+1]&0xff));
}

}
}else
if(-2==temp[0]&&-1==temp[1]){
for(int i=2;i<temp.length;i+=2){  
if((temp[i+1]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i+1]&0xff));
}else{
sb.append(Integer.toHexString(temp[i+1]&0xff));
}
if((temp[i]&0xff)<16){
sb.append("0"+Integer.toHexString(temp[i]&0xff));
}else{
sb.append(Integer.toHexString(temp[i]&0xff));
}
}
}
return sb.toString();
} catch (UnsupportedEncodingException e) {

e.printStackTrace();
}
return s;
    }
   

相关推荐

    汉字与unicode编码(十六进制)对照表

    在实际应用中,开发者会使用这些对照表来实现汉字与编码之间的转换。例如,在编程中,如果需要查找某个汉字对应的Unicode编码,可以快速定位到相应位置;反之,若接收到一个Unicode编码,也能通过对照表找出对应的...

    jother编码解码工具

    "jother编码解码工具"可能实现了将文字字符转换为其对应的Unicode编码,然后再将这些编码转换为特定的标点符号。Unicode编码是一种国际标准,它为世界上几乎所有的字符分配了一个唯一的数字,使得不同的系统和语言...

    java 中文Unicode转换

    总的来说,Java提供了丰富的API来处理Unicode编码,无论是转换中文字符还是从Unicode码点恢复字符,都可以轻松实现。在处理多语言文本时,理解和熟练运用Unicode编码是至关重要的。通过上述方法,你可以有效地在Java...

    易语言源码易语言Unicode汉字编码表源码.rar

    通过分析这个"易语言Unicode汉字编码表源码",我们可以学习到如何在易语言环境中实现Unicode编码与解码,这对于进行跨平台的易语言软件开发、尤其是处理大量汉字内容的软件来说,是非常关键的知识点。

    汉字编码与解码程序

    本程序"汉字编码与解码程序"专注于实现这一过程,提供对汉字进行编码和解码的功能。 在计算机中,汉字编码通常涉及以下几种常见的方法: 1. **GB2312编码**:GB2312是中国最早广泛使用的汉字编码标准,也称为简体...

    unicode解码器 v2.0

    Unicode解码器v2.0是一款专为.NET开发者设计的实用工具,它能够帮助用户将那些在源代码或配置文件中以Unicode转义序列形式存在的字符转换成可读的中文字符。这款工具支持多种常见的文件格式,包括但不限于.cs(C#源...

    unicode转gb18030编码

    **GB18030** 是中国国家标准的汉字编码,全称为“信息技术—多字节编码字符集—GBK扩展A”,它是GBK编码的升级版。GB18030不仅包含了GBK中的所有汉字,还增加了大量其他语言字符,如藏文、蒙古文等。GB18030采用了单...

    unicode与gbk双向转换编码表

    1. 预处理:读取 `unicode_gbk_code.h` 文件中的映射表或加载 `不带字库编码转换表.bin` 文件。 2. 对于 Unicode 转 GBK,遍历 Unicode 码点,查找对应于该码点的 GBK 字节对,并将其写入输出流。 3. 对于 GBK 转 ...

    js对中文字符串进行gb2312编码解码

    GB2312编码是一种在中国大陆广泛使用的简体中文字符集,它包含了6763个常用汉字和一些其他字符。在JavaScript中进行GB2312编码和解码可以帮助我们正确地在不同环境间传输和展示中文字符。 首先,让我们理解GB2312...

    C语言字符编码转换UNICODE、GBK、UTF-8互相转换

    GBK是中国大陆广泛使用的汉字编码标准,它是GB2312的扩展,增加了更多的汉字和符号,尤其包含了繁体字。GBK编码使用两个字节来表示一个字符,与UNICODE相比,它不是全球通用的,主要用于中文环境。 UTF-8是一种变长...

    Unicode编码转换源码

    2. **解码**:一旦确定了输入的编码格式,就需要将其解码成Unicode码点。例如,如果是GBK编码的中文字符,需要先将GBK编码的字节流转换为码点。 3. **转换**:将Unicode码点转换为目标编码格式,如UTF-8。这涉及到...

    unicode-中文转换

    在Unicode编码体系中,中文字符通常占据两个或四个字节,这取决于具体的实现方式。 "unicode-cn.zip"这个压缩包文件似乎包含了一个名为"Unicode.exe"的应用程序,可能是用于进行Unicode编码和中文字符之间的转换...

    Unicode和汉字互相转换工具

    总的来说,这个工具提供了Unicode和汉字之间的双向转换功能,对于理解和处理中文字符编码问题具有实际应用价值。源码的提供使得开发者可以深入学习字符编码原理,以及如何在Java环境中实现这种转换。同时,预编译的...

    字符编码解码工具字符编码,解码

    理解不同编码系统的原理和应用场景,以及如何使用相应的工具进行编码解码,对于解决文本处理中的乱码问题和实现全球化软件的开发都具有重要意义。在日常工作中,掌握这些知识能帮助我们更好地理解和处理与字符编码...

    delphi2009 Unicode转汉字,汉字转Unicode源码

    总之,Delphi 2009中的Unicode和汉字转换主要涉及对Unicode字符串的编码解码操作,这可以通过Windows API或第三方库实现。理解Unicode和各种汉字编码之间的关系以及如何进行转换,对于编写能够正确处理多语言文本的...

    unicode转中文

    在压缩包文件“unicode to 中文”中,可能包含了实现这些转换的代码示例、工具或教程。学习如何进行Unicode与中文字符之间的转换,对于理解和处理涉及多种语言的编程任务至关重要。无论你是Web开发者、数据分析师...

    uni_gbk.rar_GBK Unicode_GBK编码_UNIGBK_Unicode-GBK_uni-app gbk解码

    GBK编码是中国大陆广泛使用的汉字编码标准,它是GB2312的扩展,支持更多的汉字和符号,能够覆盖大部分常用中文字符。GBK编码采用双字节表示,每个字符占用2个字节,最大可表示20902个不同的字符。 Unicode,全称...

    中文 英文 和 Unicode 互转

    中文到Unicode的转换通常是通过将中文编码(如GB2312)映射到对应的Unicode码点来实现的。这个过程可能涉及解码、转换和重新编码。例如,一个GB2312编码的汉字可以通过查找GB2312到Unicode的映射表,然后用对应的...

    汉字跟Unicode 互相 转化 C# 代码

    汉字是中文的字符,其在计算机中存储通常有两种主要方式:GBK(一种针对简体中文的扩展GBK编码)和Unicode。Unicode编码方案中,每个汉字都有一个固定的码点(Code Point),这使得不同地区的字符能够统一表示,避免...

    易语言uXXXX型编码解码

    源码文件"易语言uXXXX型编码解码源码"包含了实现这些功能的具体代码,深入研究这个源码,将有助于理解易语言处理字符编码的机制,对于开发涉及文本处理的易语言程序有着很大的帮助。通过阅读和学习,不仅可以提升...

Global site tag (gtag.js) - Google Analytics