我们上次在《细说Java中的字符和字符串(一)》说了Java中char和中文字符之间的干系,说明白char能不能存储一其中文字符,以及如何判定Java的字符串是否包括中文字符。
这次再看一下MySQL数据库中VARCHAR(N)中的N暗示什么,什么会限制N的巨细。然后,从Java的String源代码阐明一下中文字符在Java字符串里是如何存储的。
VARCHAR(N)中的N代表什么?
和第一篇雷同,我们依旧从官方文档寻找最有权威的谜底。我们从这里可以找到VARCHAR(N)中N代表什么的谜底:https://dev.mysql.com/doc/refman/8.0/en/storage-requirements.html
由于原文较量长,这里先把要害片断贴一下:
谜底就在这段文字里,我们翻译一下原文:VARCHAR 可能VARBINARY范例的列最大可存储的字节数不能高出一行的最大可存储字节数,也就是65535字节。假如VARCHAR范例的列存储的是多字节范例的字符,那么可存储的最大字符数相对就会变少。好比:utf8mb4范例的字符一个字符最大可以占到4个字节,所以一个VARCHAR范例的字段最多可以存储16383个字符(65535/4)。
重点看好比的文字,可以看出来MySQL的VARCHAR(N)中的N代表的是几多个字符(Character),而Character的对付差异的编码又代表什么呢?
一起看一下另一篇MySQL官网文档的内容:https://dev.mysql.com/doc/refman/8.0/en/charset-unicode.html
VARCHAR(N)的N值岂论是哪种编码,都对应到了字符级别,也就是这个列可以存下N个字符。MySQL的utf8和Java里的utf8纷歧样,MySQL的实现里utf8最大只答允存储3个字节。从上一篇我们知道,utf8编码最长有4个字节,那么4个字节的utf8字符MySQL的utf8范例是存不下的。
所以,昆山软件开发,当需要存储emoji心情符的时候,我们需要用utf8mb4范例的字符集才气存下。MySQL的utf8mb4字符集才气包括全部utf8字符,而MySQL的utf8字符集是一个不完全版本的utf8字符集。
总结一下:MySQL中VARCHAR(N)字段范例里边的N代表最大可以存几多个字。假如选的是中文编码,好比utf8编码,那么N代表最大可以存几多个汉字。
VARCHAR(N)中的N可以是多大?
N的最大值主要限制因素是MySQL的最大行巨细,原文地点在:https://dev.mysql.com/doc/refman/8.0/en/column-count-limit.html。
MySQL的行巨细最大不能高出65535bytes,是说MySQL的一行里所有列占用字节数的和不能高出65535bytes。BLOB和TEXT较量出格,他们只占用9个字节巨细,真实数据存储在行外。
MySQL的uft8范例最多占3个字节,MySQL答允的最大字符数是21844(65535/3-1),utf8mb4最多占4个字节,所以答允的最大字符数是16382(65535/4-1)。这个数字有一点点毛病,因为VARCHAR范例需要1到2个字节存储length,所以最大字符数可以凭据减1计较。
从Java String阐明Java的char和中文的干系
String有三类结构函数,一类是传入byte[],一类是传入char[],一类是传入int[],如下:
public String(byte bytes[], int offset, int length);//字节约,转成字符串 public String(char value[], int offset, int count);//字符流,转成字符串。较罕用,一般用在双字节编码的字节串转String。 public String(int[] codePoints, int offset, int count);//代码点,转成字符串
String内部数据存储利用的是char[]存储,byte 是8字节,char是16字节,int是32字节。所以byte[]参数的结构函数会把byte[]理会成正确的代码点,也就是int范例,再转换成char[]范例存到String内部。
所以,我们这里只阐明int[]范例入参的结构函数,来看一下String是如何把int范例的代码点转成char[]存起来的。从这里,也能看出来一个2个字节的Unicode和一个2个以上字节的Unicode代码点是怎么存到String里的。
public String(int[] codePoints, int offset, int count) { //.... // Pass 1: 计较int[]转成char[]后的长度 int n = count; for (int i = offset; i < end; i++) { int c = codePoints[i]; if (Character.isBmpCodePoint(c))//假如是BMP范畴的代码点,那么计数加1 continue; else if (Character.isValidCodePoint(c))//假如是超出BMP范畴的有效代码点,那么计数加2 n++; else throw new IllegalArgumentException(Integer.toString(c)); } // Pass 2: 利用int[]的值填充char[] final char[] v = new char[n]; for (int i = offset, j = 0; i < end; i++, j++) { int c = codePoints[i]; if (Character.isBmpCodePoint(c))//假如是BMP范畴的代码点,直接插手char[] v[j] = (char)c; else//假如是超出BMP范畴的有效代码点,劳务派遣管理系统,那么转成2个char,劳务派遣管理系统,存入char[] Character.toSurrogates(c, v, j++); } this.value = v; }