从byte[]和char[]的不同,看字符串的编码问题

2023-10-22 15:12:30 浏览数 (2)

从byte[]和char[]的不同,看字符串的编码问题

一、概述

众所周知:

  1. byte 是字节数据类型 ,是有符号型的,占1 个字节;大小范围为-128—127 。
  2. char 是字符数据类型 ,是无符号型的,占2字节(Unicode码 );大小范围 是0—65535 ;

char是一个16位二进制的Unicode字符,JAVA用char来表示一个字符 。

可能看到上面这句话的时候,往往不会在意char是不是Unicode字符,当时它却是我们理清楚编码的关键。

二、编码类型

一说到编码,就会想到GBK和utf8,到底这些编码都是干什么的呢?

  1. ASCII:严格来说,我们提到编码的时候,没必要说ASCII的,因为它不支持中文,它就不会被我们日常拿来用做字符串的编码。它对应的Charset是US-ASCII
  2. GBK:国人就定义了一套编码规则,中文两个字节,英文一个字节,注意是字节。它对应的Charset是GBK。
  3. UTF-8:UTF是 Unicode Translation Format,即把Unicode转做某种格式的意思,针对Unicode的一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,中文三个字节,英文一个字节。它对应的Charset是UTF-8。
  4. Unicode:Unicode只是一个符号集,它只规定了符号的二进制代码,却没有规定这个二进制代码应该如何存储。UTF-8是Unicode的实现方式之一。如果用Charset.forName("Unicode"),结果是UTF_16。

三、byte[]和char[]

byte[]是字节数组,而char[]是字符数组。

  1. 一个英文,存储在byte[]中,长度是1,存储在char[]也是1。
  2. 一个中文,存储在byte[]中,utf8长度是3,存储在char[]是1。
3.1 String和StringBuilder看字节数组和字符数组

String的构造方法有根据字符数组和字节数组创建字符对象,StringBuilder中只有添加字符数组的方法。 String:

代码语言:javascript复制
public String(char value[]) {
    this.value = Arrays.copyOf(value, value.length);
}
public String(byte bytes[]) {
    this(bytes, 0, bytes.length);
}
public String(byte bytes[], String charsetName)
            throws UnsupportedEncodingException {
    this(bytes, 0, bytes.length, charsetName);
}
public String(byte bytes[], Charset charset) {
    this(bytes, 0, bytes.length, charset);
}

StringBuilder:

代码语言:javascript复制
public StringBuilder append(char[] str) {
    super.append(str);
    return this;
}

所以,有时候会疑惑,为什么StringBuilder不需要考虑编码的问题,这是因为StringBuilder本身是一个char[] value.

而String本身也是个char value[],但是却将byte[]装成了char[]:

代码语言:javascript复制
static char[] decode(String charsetName, byte[] ba, int off, int len)
        throws UnsupportedEncodingException
{
    StringDecoder sd = deref(decoder);
    String csn = (charsetName == null) ? "ISO-8859-1" : charsetName;
    if ((sd == null) || !(csn.equals(sd.requestedCharsetName())
                          || csn.equals(sd.charsetName()))) {
        sd = null;
        try {
            Charset cs = lookupCharset(csn);
            if (cs != null)
                sd = new StringDecoder(cs, csn);
        } catch (IllegalCharsetNameException x) {}
        if (sd == null)
            throw new UnsupportedEncodingException(csn);
        set(decoder, sd);
    }
    return sd.decode(ba, off, len);
}

所以,字符串就是存储的字符,不叫字节串,字节转字符需要指定编码,字符转字节也需要指定编码。

3.1 InputStream和InputStreamReader看字节数组和字符数组

InputStream无论是网络流还是文件流,都是不需要自定编码,如:

代码语言:javascript复制
public FileInputStream(String name) throws FileNotFoundException {
    this(name != null ? new File(name) : null);
}
public ByteArrayInputStream(byte buf[]) {
    this.buf = buf;
    this.pos = 0;
    this.count = buf.length;
}

因为它本身就是字节,属于存储属性的字节,已经有编码含义。

InputStreamReader需要指定编码:

代码语言:javascript复制
public InputStreamReader(InputStream in, String charsetName)
        throws UnsupportedEncodingException
{
    super(in);
    if (charsetName == null)
        throw new NullPointerException("charsetName");
    sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}

类似与String, 它需要将字节转成字符,就需要指定编码。

四、结论

一个字就是一个字符,一个字可以有多个字节。不同的编码下,一个字的字节数不同。

0 人点赞