0%

JavaScript计算不同编码下字符串字节长度

前言

默认的JavaScript计算字符串长度是按照字符数计算的,无论是1个汉字还是1个英文都计算为1个单位长度,但在某些环境中该值可能不够准确,例如在UTF-8中一个中文字符为3~4个字节,ZHS16GBK中为2个字节,这就需要用字符串的字节长度来准确描述一个字符串的长度。那么如何准确表达特定编码下的字符串字节长度呢?下面总结了一些方法。

计算UTF-8编码的字符串字节长度

JavaScript并没有提供原生的UTF-8字符长度计算的方法,因此需要自行计算,代码如下:

1
2
3
4
function lengthInUtf8Bytes(str) {
var m = encodeURIComponent(str).match(/%[89ABab]/g);
return str.length + (m ? m.length : 0);
}

要理解上述方法为何能正确运行,需要了解UTF-8编码的规范。

由于UTF-8为变长字节编码方式,对于任意UTF-8编码的字符,如果该字符只有一个字节,则该字节最高二进制位为0;如果该字符由多个字节组成,则该字符的第一个字节从最高位开始连续的二进制1个数代表了该字符的字节数,其余的各字节均以10开头,如下表:

字节数 有效位数 第一个字节 第二个字节 第三个字节
1 7 0xxxxxxx
2 11 110xxxxx 10xxxxxx
3 16 1110xxxx 10xxxxxx 10xxxxxx

因此可以得出以下规律:

  1. 如果一个字符为单字节字符,那么它一定只有一个字节,且最高位一定为0;
  2. 如果一个字符为多字节字符,那么它的第一个字节一定是以16进制的C、D、E、F开头,且该字符接下来所有的字节均以二进制10开头,也就是16进制的8、9、A、B开头。

也就是说,只要一个字符串为UTF-8编码且经过encodeURIComponent()方法编码后,那么就可以利用URI字符串的特性,通过上述的规律得到一个字符正确的字节数,判断方法如下:

  1. 当一个字符为单字节字符,其字符长度一定为1;
  2. 当一个字符为多字节字符,其第一个字节后的所有字节一定匹配%8*, %9*, %A*, %B*的任意一种,其中*为任意十六进制字符。这是因为08H = 1000B, 09H = 1001B, 0AH = 1010B, 0BH = 1011B,它们都符合二进制由10开头的特性。那么在找到匹配上述集合的元素后,只要在找到的元素个数上加1,就可以准确计算该字符的字节长度。

在上述计算代码中,对于字符串内的单个字符,使用/%[89ABab]/g正则表达式匹配到上述判断方法(2)中的字节后,再加上字符串的字符个数,即可得到准确的UTF-8字节长度。

计算ZHS16GBK编码的字符串字节长度

由于一些数据库(如Oracle)默认使用ZHS16GBK编码作为中文字符的编码,该编码和UTF-8不同,一个中文字符由2个字节组成,数字、英文字符为1个字节。由于ZHS16GBK为定长字节编码,因此计算起来要简单很多。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
function lengthInZhs16GbkBytes(str) {
var len = 0;
for (var i = 0; i < str.length; i++) {
var a = str.charAt(i);
if (a.match(/[^\x00-\xff]/ig) != null) {
len += 2;
}
else {
len += 1;
}
}
return len;
}

其中/[^\x00-\xff]/ig正则表达式用于匹配单个字符是否由2个字节以上组成。若匹配成功,则总字节数+2;否则为单字节字符,字节数+1。

参考来源

https://stackoverflow.com/questions/5515869/string-length-in-bytes-in-javascript/