原文1链接:[Charset]UTF-8, UTF-16, UTF-16LE, UTF-16BE的区别 原文2链接:UTF8,UTF16,UTF32,UTF16-LE,UTF16-BE,GBK 之间的转换

文章1

最近遇到的麻烦事 
charset里的问题, 一般我们都用unicode来作为统一编码, 但unicode也有多种表现形式

首先, 我们说的unicode, 其实就是utf-16, 但最通用的却是utf-8, 
原因: 我猜大概是英文占的比例比较大, 这样utf-8的存储优势比较明显, 因为utf-16是固定16位的(双字节), 而utf-8则是看情况而定, 即可变长度, 常规的128个ASCII只需要8位(单字节), 而汉字需要24位

UTF-16, UTF-16LE, UTF-16BE, 及其区别BOM 
同样都是unicode, 为什么要搞3种这么麻烦? 
先说UTF-16BE (big endian), 比较好理解的, 俗称大头 
比如说char 'a', ascii为 
0x61, 那么它的utf-8, 则为 [0x61], 但utf-16是16位的, 所以为[0x00, 0x61] 
再说UTF-16LE(little endian), 俗称小头, 这个是比较常用的 
还是char 'a', 它的代码却反过来: [0x61, 0x00], 据说是为了提高速度而迎合CPU的胃口, CPU就是这到倒着吃数据的, 这里面有汇编的知识, 不多说 
然后说UTF-16, 要从代码里自动判断一个文件到底是UTF-16LE还是BE, 对于单纯的英文字符来说还比较好办, 但要有特殊字符, 图形符号, 汉字, 法文, 俄语, 火星语之类的话, 相信各位都很头痛吧, 所以, unicode组织引入了BOM的概念, 即byte order mark, 顾名思义, 就是表名这个文件到底是LE还是BE的, 
其方法就是, 在UTF-16文件的头2个字节里做个标记: LE [0xFF, 0xFE], BE [0xFE, 0xFF]

理解了这个后, 在java里遇到utf-16还是会遇到麻烦, 因为要在文件里面单独判断头2个再字节是很不流畅的

小结:

Java代码  
  1. InputStreamReader reader=new InputStreamReader(fin, charset)

1. 如果这个UTF-16文件里带有BOM的话, charset就用"UTF-16", java会自动根据BOM判断LE还是BE, 如果你在这里指定了"UTF-16LE"或"UTF-16BE"的话, 猜错了会生成乱七八糟的文件, 哪怕猜对了, java也会把头2个字节当成文本输出给你而不会略过去, 因为[FF FE]或[FE FF]这2个代码没有内容, 所以, windows会用"?"代替给你 
2. 如果这个UTF-16文件里不带BOM的话, 则charset就要用"UTF-16LE"或"UTF-16BE"来指定LE还是BE的编码方式

另外, UTF-8也有BOM的, [0xEF, 0xBB, 0xBF], 但可有可无, 但用windows的notepad另存为时会自动帮你加上这个, 而很多非windows平台的UTF8文件又没有这个BOM, 真是难为我们这些程序员啊

错误的例子 
1. 文件A, UTF16格式, 带BOM LE, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16le") 
会多输出一个"?"在第一个字节, 原因: java没有把头2位当成BOM

2. 文件A, UTF16格式, 带BOM LE, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16be") 
会出乱码, 原因: 字节的高低位弄反了, 'a' 在文件里 [0x61, 0x00], 但java以为'a'应该是[0x00 0x61]

3. 文件A, UTF16格式, 带BOM BE, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16le") 
会出乱码, 原因: 字节的高低位弄反了, 'a' 在文件里 [0x00, 0x61], 但java以为'a'应该是[0x61 0x00]

4. 文件A, UTF16格式, 带BOM BE, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16be") 
会多输出一个"?"在第一个字节, 原因: java没有把头2位当成BOM

5. 文件A, UTF16格式, LE 不带BOM, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16") 
会出乱码, 因为utf-16对于java来说, 默认为be(1.6JDK, 以后的说不准) 
但windows的notepad打开正常, 因为notepad默认为le, - -#

6. 文件A, UTF16格式, BE 不带BOM, 
InputStreamReader reader=new InputStreamReader(fin, "utf-16") 
恭喜你, 蒙对了 
但winodws的notepad打开时, 每个字符中间都多了一个" ", 因为notepad把它当成ASNI了

在windows下输出unicode文件 
通过java出来unicode文件, 也容易混淆

Java代码  
  1. FileOutputStream fout=new FileOutputStream(file);
  2. OutputStreamWriter writer=new OutputStreamWriter(fout, charset);

1. charset为"UTF-16"时, java会默认添加BOM [0xFE, 0xFF], 并以BE的格式编写byte 
2. charset为"UTF-16BE"时, java不会添加BOM, 但编码方式为 BE 
3. charset为"UTF-16LE"时, java不会添加BOM, 但编码方式为 LE

以上通过 test.getByte("utf-16"), test.getByte("utf-16be"), test.getByte("utf-16le") 可以验证

而windows的notepad默认的unicode为 LE, 并带BOM, 
所以, 推荐输出 UTF-16LE, 并人为添加BOM, 即:

Java代码  
  1. byte[] bom={-1, -2};    //FF FE, java的byte用的是补码, 验证: b=127, b+=1, 而b=-128
  2. fout.write(bom);

文章2

Unicode是Unicode.org制定的编码标准,目前得到了绝大部分操作系统和编程语言的支持。Unicode.org官方对Unicode的定义是:Unicode provides a unique number for every character。可见,Unicode所做的是为每个字符定义了一个相应的数字表示。比如,“a“的Unicode值是0x0061,“一”的Unicde值是0x4E00,这是最简单的情况,每个字符用2个字节表示。

Unicode.org定义了百万个以上的字符,如果将所有的字符用统一的格式表示,需要的是4个字节。“a“的Unicode表示就会变成0x00000061,而“一“的Unicode值是0x00004E00。实际上,这就是UTF32,Linux操作系统上所使用的Unicode方案。

但是,仔细分析可以发现,其实绝大部分字符只使用2个字节就可以表示了。英文的Unicode范围是0x0000-0x007F,中文的Unicode范围是0x4E00-0x9F**,真正需要扩展到4个字节来表示的字符少之又少,所以有些系统直接使用2个字节来表示Unicode。比如Windows系统上,Unicode就是两个字节的。对于那些需要4个字节才能表示的字符,使用一种代理的手法来扩展(其实就是在低两个字节上做一个标记,表示这是一个代理,需要连接上随后的两个字节,才能组成一个字符)。这样的好处是大量的节约了存取空间,也提高了处理的速度。这种Unicode表示方法就是UTF16。一般在Windows平台上,提到Unicode,那就是指UTF16了。
        
        至于UTF16-LE和UTF16-BE,与计算机的CPU构架有关。LE指Little Endian,而BE指Big Endian。关于这方面的信息,网上有很多相关的帖子。我们一般的X86系统都是Little Endian的,可以认为UTF16=UTF16-LE.

由于对于欧洲和北美,实际上使用的编码范围在0x0000-0x00FF之间,只需要一个字符就可以表示所有的字符。即使是使用UTF16来作为内存的存取方式,还是会带来巨大的空间浪费,因此就有了UTF8的编码方式。这是一种很灵活的编码,对于只需要1个字节的字符,就使用一个字节,对于中日韩等原本需要两个字节才能表示的字符,则通过一个UTF16-UTF8的算法实现相互之间的转换(一般需要3个字节才能表示),而对于需要4个字节才能表示的字符,UTF8可以扩展到6个字节每个字符。UTF8使用的算法很有意思,大致映射关系如下:
        UTF-32                                    UTF8
      0x00000000 - 0x0000007F          0xxxxxxx 
         0x00000080 - 0x000007FF           110xxxxx 10xxxxxx 
         0x00000800 - 0x0000FFFF          1110xxxx 10xxxxxx 10xxxxxx 
        0x00010000 - 0x001FFFFF          11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 
         0x00200000 - 0x03FFFFFF          111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
         0x04000000 - 0x7FFFFFFF          1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 
        可以发现这和IP的分址算法很是相像。
        由于UTF8可以方便的转换为UTF16和UTF32(不需要码表,转换算法可以在Unicode.org上找到C代码)。而且UTF8在每个操作系统平台上的实现都是一样的,也不存在跨平台的问题,所以UTF8成为跨平台的Unicode很好的解决方案。当然,对于中文来说,由于每个字符需要3个字节才能表示,还是有点浪费的。
 
UTF8文本头为 EF BB BF
UTF16 文本头: Big-Endian的FEFF; 表明这个字节流是;Little-Endian的FFFE
  int convertUTF8UTF16(unsigned char* utf8, int& size8, char* utf16, int& size16)

{

int count =0, i;

char tmp1, tmp2;

unsigned short int integer;

unsigned short int *p;

for(i=0;i<size8;i+=1)
    {

p = (unsigned short int*)&utf16[i];

if( utf8[count] < 0x80)

{

// <0x80

integer = utf8[count];

count++;

}

else if( (utf8[count] < 0xDF) && (utf8[count]>=0x80))

{

integer = utf8[count] & 0x1F;

integer = integer << 6;

integer += utf8[count+1] &0x3F;

count+=2;

}

else if( (utf8[count] <= 0xEF) && (utf8[count]>=0xDF))

{

integer = utf8[count] & 0x0F;

integer = integer << 6;

integer += utf8[count+1] &0x3F;

integer = integer << 6;

integer += utf8[count+2] &0x3F;

count+=3;

}

else

{

printf("error!/n");

}

*p = integer;

}

size8 = count;

size16 = i;
      return size16;

}
  int convertUTF16UTF8(char* utf16, int& size16, char* utf8, int& size8)

{

int i=0, count=0;

char tmp1, tmp2;

unsigned short int integer;

for(i=0;i<size16;i+=2)
    {

integer = *(unsigned short int*)&utf16[i];

if( integer<0x80)

{

utf8[count] = utf16[i] & 0x7f;

count++;

}

else if( integer>=0x80 && integer<0x07ff)

{

tmp1 = integer>>6;

utf8[count] = 0xC0 | (0x1F & integer>>6);

utf8[count+1] = 0x80 | (0x3F & integer);

count+=2;

}

else if( integer>=0x0800 )

{

tmp1 = integer>>12;

utf8[count] = 0xE0 | (0x0F & integer>>12);

utf8[count+1] = 0x80 | ((0x0FC0 & integer)>>6);

utf8[count+2] = 0x80 | (0x003F & integer);

count += 3;

}

else

{

printf("error/n");

}

}
      size16 = i;

size8  = count;

return count;

}

UTF-8 转 Unicode, Unicode 转 GBK, UTF-8 转 GBK
#include <windows.h>
#include <stdio.h>

void main() {

//三个不同版本的老徐
unsigned char utf8[] = "/xe8/x80/x81/xe5/xbe/x90";
unsigned char unicode[] = "/x01/x80/x90/x5f";
unsigned char ansi[] = "/xc0/xcf/xd0/xec";

int len;

//UTF-8转UNICODE
len = MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8, -1, NULL,0);
WCHAR * wszUtf8 = new WCHAR[len+1];
memset(wszUtf8, 0, len * 2 + 2);
MultiByteToWideChar(CP_UTF8, 0, (LPCSTR)utf8, -1, wszUtf8, len);

MessageBoxW(NULL, (const wchar_t*)wszUtf8, NULL, MB_OK);

//UNICODE转ANSI,实际上经过两次转换 UTF-8 已经变成了 GBK 编码
len = WideCharToMultiByte(CP_ACP, 0, wszUtf8, -1, NULL, 0, NULL, NULL); 
char *szGBK=new char[len + 1];
memset(szGBK, 0, len + 1);
WideCharToMultiByte (CP_ACP, 0, wszUtf8, -1, szGBK, len, NULL,NULL);

MessageBoxA(NULL, (const char*)szGBK, NULL, MB_OK);

delete[] szGBK;
delete[] wszUtf8;

}

Unicode中的UTF-8, UTF-16, UTF-16LE, UTF-16BE编码及转换 | Java基础相关推荐

  1. 在java中为什么_属性绑定到类型_方法绑定到对象_13. Java基础之类型信息(RTTI和反射)...

    一. Java反射机制介绍 Java 反射机制.通俗来讲呢,就是在运行状态中,我们可以根据"类的部分已经的信息"来还原"类的全部的信息".这里"类的部 ...

  2. html utf 8编码转换器,utf8和gb2312编码在线转换工具

    utf8和gb2312编码在线转换工具_网页代码站(www.webdm.cn) style="Z-INDEX: 1000; VISIBILITY: hidden; WIDTH: 1px; P ...

  3. Unicode中UTF-8与UTF-16编码详解

    概述 本文通过介绍Unicode编码以及对应的两种编码方式UTF-8和UTF-16,让读者能够了解关于字符串编码的相关知识,同时能够弄清楚Unicode和UTF-8和UTF-16之间的关系. 本文的主 ...

  4. [转载]Unicode中对中文字符的编码

    以前写过一篇贴子是写中文在unicode中的编码范围 unicode中文范围,但写的不是很详细,今天再次研究了下unicode,并给出详细的unicode取值范围. 本次研究的unicode对象是un ...

  5. 中文在unicode中的编码范围

    喜欢古典小说的 可以关注  头条号 古典小说  公众号 古典小说网 喜欢编程的     可以关注 头条号 讨厌编程 以前写过一篇贴子是写中文在unicode中的编码范围unicode中文范围,但写的不 ...

  6. 7.某计算机的控制器采用微程序控制方式,微指令中的操作控制字段的16位采用混合表示法,其中用11位采用直接表示法,另外5位分为3位和2位的编码表示法,则此格式的微指令最多可表示多少个微指令?

    7.某计算机的控制器采用微程序控制方式,微指令中的操作控制字段的16位采用混合表示法,其中用11位采用直接表示法,另外5位分为3位和2位的编码表示法,则此格式的微指令最多可表示多少个微指令? 解: - ...

  7. 中国女足在2022女足亚洲杯决赛中击败韩国队,时隔16年再夺亚洲杯冠军。

    新华社快讯:中国女足在2022女足亚洲杯决赛中击败韩国队,时隔16年再夺亚洲杯冠军. 在北京时间6日晚结束的女足亚洲杯决赛中,中国女足3:2逆转韩国女足夺冠!比赛中,韩国队先入两球,下半场唐佳丽.张琳 ...

  8. [工具]-C语言中字符串的形式打印16进制数据

    C语言中字符串的形式打印16进制数据. 这样写,输出更直观些 #define PRINT_BUF_MAX (32) #define TAG_STRING_MAX (32) static void pr ...

  9. html 图片 保持长宽比,实现图片在页面中宽高一直保持16:9比例的方法

    实现图片在页面中宽高一直保持16:9比例的方法 发布时间:2020-08-31 14:25:10 来源:亿速云 阅读:133 作者:小新 小编给大家分享一下实现图片在页面中宽高一直保持16:9比例的方 ...

最新文章

  1. SOC在安全方面的真实价值
  2. C#怎么通过一个按钮Button,实现点击这个按钮可以打开一个文件或者文件夹?
  3. “金星生命论”乌龙事件新进展:那只是二氧化硫而已
  4. asp接收ajax乱码_Asp中处理AJAX乱码问题总结
  5. Visual Studio 竖向选择功能
  6. 「LibreOJ NOIP Round #1」旅游路线
  7. 泽元网站内容管理系统 (简称ZCMS)
  8. Bailian4040 买书问题【Ad Hoc】
  9. js src 变量_人人都能看懂的鸿蒙 “JS 小程序” 数据绑定原理
  10. 原始点,姜治百病理论,害人不浅
  11. java生成pdf文件并打印
  12. 教师资格证报名网站显示内部服务器错误,2020上半年教师资格考试报名缴费提示错误怎么办?缴费问题汇总...
  13. MySQL 报错1449:The user specified as a definer (’mysql.infoschema‘@’localhost‘) does not exist
  14. FPGA学习笔记(1)简单的时序逻辑电路——流水灯
  15. Ubuntu下web开发,php编辑器推荐
  16. ActiveMQ连接数过多,导致ActiveMQ无法正常接入数据
  17. git tag创建、远程推送、回退以及强推push -f
  18. 主动变被动9个例句_【语法课堂】全面了解被动语态的特殊用法考点总结
  19. 这本书能让你减肥/戒烟/工作脱困/摆脱债务/重掌生活
  20. Struts 往jsp页面传值的三种方式 以及获取Jsp页面值得一种方式

热门文章

  1. 软件设计师选择题真题知识点归纳
  2. 开源规则引擎比较_开源物联网平台ThingsBoard
  3. Android的.9图片使用问题
  4. AOP层层递进 第一部分 代理
  5. 基于极化码(Polar Code)的加密
  6. 哪种一致性哈希算法才是解决分布式缓存问题的王者?
  7. iOS多线程简单理解
  8. 无论创业还是做人,你都需要知道什么是MVPPMF
  9. 经验模态分解与Python调用实例
  10. KGC的衡量指标(MR、MRR、Hits@n)