在Java中,一个char类型变量能否存储一个中文汉字?
答案是肯定的,显然,Java中一个char变量能够存储一个中文汉字
char ch = '中';
String chstr = "你好"
System.out.println(chstr.length()); // 2
因为在Java中,字符是以Unicode的格式保存的,一个char类型对应一个16位的Unicode码
public static void main(String[] args) throws UnsupportedEncodingException {
char ch = '中';
String str="中国";
System.out.println(Integer.toHexString(ch)); // 4e2d
System.out.println(strToHexString(str)); //4e 2d 56 fd
}
//将String转换为16进制码
public static String strToHexString(String str) throws UnsupportedEncodingException {
StringBuilder sb = new StringBuilder();
byte[] units = str.getBytes("UTF-16BE");
for(int i=0;i<units.length;++i){
if(i!=0) sb.append(" ");
sb.append(Integer.toHexString(units[i]&0xff)+"");
}
return sb.toString();
}
String.getBytes(codeFormation)方法能够将String转化为对应字符编码的byte数组,可以发现char的真正存储格式和UTF-16BE是相同的,那么尝试其他几种格式呢?
编码 | 内容 | 长度(字节) |
字符'A'的UTF-16BE编码 | 00 41 | 2 |
字符'A'的UTF-16LE编码 | 41 00 | 2 |
字符'A'的UTF-16编码(BE) | FE FF 00 41 | 2+2 |
字符'A'的UTF-16编码(LE) | FF FE 41 00 | 2+2 |
字符'中'的UTF-8编码 | 41 | 1 |
字符'A'的UTF-32编码 | 00 00 00 41 | 4 |
字符'A'的GBK编码 | 41 | 1 |
字符'A'的GB2312编码 | 41 | 1 |
字符'A'的ASCII编码 | 41 | 1 |
编码 | 内容 | 长度(字节) |
字符'中'的UTF-16BE编码 | 4E 2D | 2 |
字符'中'的UTF-16LE编码 | 2D 4E | 2 |
字符'中'的UTF-16编码(BE) | FE FF 4E 2D | 2+2 |
字符'中'的UTF-16编码(LE) | FF FE 2D 4E | 2+2 |
字符'中'的UTF-8编码 | E4 B8 AD | 3 |
字符'中'的UTF-32编码 | 00 00 4E 2D | 4 |
字符'中'的GBK编码 | D6 D0 | 2 |
字符'中'的GB2312编码 | D6 D0 | 2 |
字符'中'的ASCII编码 | 3F(表示无) |
|
可见Java中一个字符对应的是一个Unicode码,而一个String由Unicode码组成,如果转换成UTF-16的话,需要BOM(Byte Order Mark)的字符(big endian:feff,little endian:ff fe)来表明编码的格式,getbtyes()默认是转为UTF-8格式
那么,一个char变量一定能完整的存储下一个字符么?
答案是否定的
在这里介绍下编码的知识
- Unicode编码(Universal Mutiple-Octet Coded Character Set,简称UCS,俗称Unicode)
- Unciode编码定义了所有字符的数字表示,每个数字表示称之为码点(Code Point)
- 一般以 U+ABCD 来表示
- 第一版为UCS-2(2个字节),第二版为UCS-4(4个字节)
- 目前UCS已经扩展到了21位(U+0000 ~ U+10 FFFF)
- 编码空间被分为17个平面(plane),0号平面(U+0000 ~ U+FFFF)称之为基本多语言平面(Basic Multilingual Plane),包含了常用语言字符,其他平面为辅助字符(特殊符号,emoji)
- UTF(Unicode Transformation Format)
- UTF定义了码点在计算机中的二进制存储形式
- 码点按照UTF定义的格式,转换为的二进制串的单元称之为码元(Code Unit),一个码元的大小对应了UTF的后缀
- UTF-16
- 一般编程语言的字符机内转换格式
- 长度可变,代码单元大小为2个字节
- 基本多语言平面中,一个代码点由一个代码单元表示;辅助字符平面中,一个代码点由两个代码单元表示
- UTF-8
- 最常用的转换格式,存储和传输效率最高
- 长度由1~4,兼容ASCII码
- 0~127的码点转化为1个字节,其余字符转换为2~4个字节(中文一般3个字节)
在Java5.0之后,添了辅助字符,需要2个char类型存储,
比如 '?',对应的unicode码为 D834 DF06,在Java中转换为2个码元
String normalStr="你好Hello";
System.out.println(normalStr+",代码单元数量:"+normalStr.length()); // 7
System.out.println(normalStr+",代码点数量:"+normalStr.codePointCount(0,normalStr.length())); // 7
char[] c = Character.toChars(Integer.parseInt("1D306", 16));
String supplyStr=new String(c)+"是一个辅助字符"; // ”?是一个辅助字符”
System.out.println(supplyStr+",代码单元数量:"+supplyStr.length()); // 9
System.out.println(supplyStr+",代码点数量:"+supplyStr.codePointCount(0,supplyStr.length())); 8
总结:Java中的char类型,包含两个字节,存储一个UTF-16格式的代码单元
一个char类型能表示常用语言的字符,但辅助字符需要两个代码单元来表示
附上:一个展示编码,代码点,代码单元的小程序
package com.stk.learnjava;
import java.io.UnsupportedEncodingException;
/**
* Created by stk on 2016/8/16 0016.
*
*/
public class LearnString {
public static void main(String[] args) throws UnsupportedEncodingException {
learnCodePoint();
learnUnicode();
learnStringAPI();
}
private static void learnStringAPI()
{
String substr="abc";
StringBuilder sb=new StringBuilder();
sb.append(true);
sb.append('a');
sb.append("str");
System.out.println(sb.toString());
}
private static void learnCodePoint()
{
System.out.println("------关于代码点和代码单元------");
String normalStr="你好Hello";
System.out.println(normalStr+",代码单元数量:"+normalStr.length());
System.out.println(normalStr+",代码点数量:"+normalStr.codePointCount(0,normalStr.length()));
System.out.println(normalStr+",第一个代码单元:"+normalStr.charAt(0));
System.out.println(normalStr+",第一个代码点:"
+"0x"+Integer.toHexString(normalStr.codePointAt(normalStr.offsetByCodePoints(0,0)))+"("
+new String(Character.toChars(normalStr.codePointAt(normalStr.offsetByCodePoints(0,0))))+")");
System.out.println(normalStr+",第二个代码单元:"+normalStr.charAt(1));
System.out.println(normalStr+",第二个代码点:"
+"0x"+Integer.toHexString(normalStr.codePointAt(normalStr.offsetByCodePoints(0,1)))+"("
+new String(Character.toChars(normalStr.codePointAt(normalStr.offsetByCodePoints(0,1))))+")");
char[] c = Character.toChars(Integer.parseInt("1D306", 16));
String supplyStr=new String(c)+"是一个辅助字符";
System.out.println(supplyStr+",代码单元数量:"+supplyStr.length());
System.out.println(supplyStr+",代码点数量:"+supplyStr.codePointCount(0,supplyStr.length()));
System.out.println(supplyStr+",第一个代码单元:"+supplyStr.charAt(0));
System.out.println(supplyStr+",第一个代码点:"
+"0x"+Integer.toHexString(supplyStr.codePointAt(supplyStr.offsetByCodePoints(0,0)))+"("
+new String(Character.toChars(supplyStr.codePointAt(supplyStr.offsetByCodePoints(0,0))))+")");
System.out.println(supplyStr+",第二个代码单元:"+supplyStr.charAt(1));
System.out.println(supplyStr+",第二个代码点:"
+"0x"+Integer.toHexString(supplyStr.codePointAt(supplyStr.offsetByCodePoints(0,1)))+"("
+new String(Character.toChars(supplyStr.codePointAt(supplyStr.offsetByCodePoints(0,1))))+")");
}
private static void learnUnicode() throws UnsupportedEncodingException {
System.out.println("------关于字符编码-----");
char ech='A';
System.out.println("字符'A'Java中的16进制码:"+Integer.toHexString(ech));
String estr="A";
System.out.println("字符'A'的UTF-16BE编码:"+byteToHex(estr.getBytes("UTF-16BE")));
System.out.println("字符'A'的UTF-16LE编码:"+byteToHex(estr.getBytes("UTF-16LE")));
System.out.println("字符'A'的UTF-16编码:"+byteToHex(estr.getBytes("UTF-16")));
System.out.println("字符'A'的UTF-8编码:"+byteToHex(estr.getBytes("UTF-8")));
System.out.println("字符'A'的UTF-32编码:"+byteToHex(estr.getBytes("UTF-32")));
System.out.println("字符'A'的GBK编码:"+byteToHex(estr.getBytes("GBK")));
System.out.println("字符'A'的GB2312编码:"+byteToHex(estr.getBytes("GB2312")));
System.out.println("字符'A'的ASCII编码:"+byteToHex(estr.getBytes("ASCII")));
char cch='中';
System.out.println("字符'中'Java中的16进制码:"+Integer.toHexString(cch).toUpperCase());
String cstr="中";
System.out.println("字符'中'的UTF-16BE编码:"+byteToHex(cstr.getBytes("UTF-16BE")));
System.out.println("字符'中'的UTF-16LE编码:"+byteToHex(cstr.getBytes("UTF-16LE")));
System.out.println("字符'中'的UTF-16编码:"+byteToHex(cstr.getBytes("UTF-16")));
System.out.println("字符'中'的UTF-8编码:"+byteToHex(cstr.getBytes("UTF-8")));
System.out.println("字符'中'的UTF-32编码:"+byteToHex(cstr.getBytes("UTF-32")));
System.out.println("字符'中'的GBK编码:"+byteToHex(cstr.getBytes("GBK")));
System.out.println("字符'中'的GB2312编码:"+byteToHex(cstr.getBytes("GB2312")));
System.out.println("字符'中'的ASCII编码:"+byteToHex(cstr.getBytes("ASCII")));
}
private static String byteToHex(byte[] bt)
{
StringBuilder sb = new StringBuilder(4);
for(int b:bt) {
sb.append(Integer.toHexString(b & 0xFF).toUpperCase());
sb.append(" ");
}
return sb.toString();
}
}