计算和截取指定字符串的UTF-8字节数

2019-08-28  本文已影响0人  躺在家里干活

由于特殊需求,需要限制输入框内容的长度,但是该长度并不是普通的字数长度而是UTF-8的字节数

UTF-8编码下 字节数

占2个字节的:〇
占3个字节的:基本等同于GBK,含21000多个汉字
占4个字节的:中日韩超大字符集里面的汉字,有5万多个
一个utf8数字占1个字节
一个utf8英文字母占1个字节

Unicode码

1个字节:Unicode码为0 - 127
2个字节:Unicode码为128 - 2047
3个字节:Unicode码为2048 - 0xFFFF
4个字节:Unicode码为65536 - 0x1FFFFF
5个字节:Unicode码为0x200000 - 0x3FFFFFF
6个字节:Unicode码为0x4000000 - 0x7FFFFFFF

计算指定字符串的字节数

function computeByteSize(str){
  for (var i=0,byte=0; i < str.length; i++) {
    var charCode = str.charCodeAt(i);
    if (0 <= charCode && charCode <= 0x7f) {
      byte += 1;
    } else if (128 <= charCode && charCode <= 0x7ff) {
      byte += 2;
    } else if (2048 <= charCode && charCode <= 0xffff) {
      byte += 3;
    } else if (65536 < charCode && charCode <= 0x1FFFFF) {
      byte += 4;
    } else if (0x200000 < charCode && charCode <= 0x3FFFFFF) {
      byte += 5;
    } else if (0x4000000 < charCode && charCode <= 0x7FFFFFFF) {
      byte += 6;
    }
  }
  return byte
}

//es6写法
function computeByteSize(str) {
  let byte=0;
  for (const chr of str) {
    //ES6提供了codePointAt方法,能够正确处理4个字节储存的字符,返回一个字符的码点
    //能正确地识别“?”,返回了它的十进制码点 134071(即十六进制的20BB7)
    let charCode = chr.codePointAt(0)
    if(0 <= charCode && charCode <= 0x7f) {
      byte += 1;
    } else if (128 <= charCode && charCode <= 0x7ff) {
      byte += 2;
    } else if (2048 <= charCode && charCode <= 0xffff) {
      byte += 3;
    } else if (65536 < charCode && charCode <= 0x1FFFFF) {
      byte += 4;
    } else if (0x200000 < charCode && charCode <= 0x3FFFFFF) {
      byte += 5;
    } else if (0x4000000 < charCode && charCode <= 0x7FFFFFFF) {
      byte += 6;
    }
  }
  return byte
}

截取指定字节数的字符串

function cutStr(str,L){
  for (var i=0,byte=0,result=''; i < str.length; i++) {
     var chr = str.charAt(i);
     var charCode = str.charCodeAt(i);
    if (0 <= charCode && charCode <= 0x7f) {
      byte += 1;
    } else if (128 <= charCode && charCode <= 0x7ff) {
      byte += 2;
    } else if (2048 <= charCode && charCode <= 0xffff) {
      byte += 3;
    } else if (65536 < charCode && code <= 0x1FFFFF) {
      byte += 4;
    } else if (0x200000 < charCode && charCode <= 0x3FFFFFF) {
      byte += 5;
    } else if (0x4000000 < charCode && charCode <= 0x7FFFFFFF) {
      byte += 6;
    }
    // 当加上当前字符以后,如果总字节长度小于等于L,则将当前字符真实的+在result后
    if(byte<=L){
      result += chr;
    }else{
      return result;
    }
  }
}

//es6 写法
function cutStr(str, L) {
  let byte=0;
  let result='';
  for(let chr of str){
    let charCode = chr.codePointAt(0)
    if (0 <= charCode && charCode <= 0x7f) {
      byte += 1;
    } else if (128 <= charCode && charCode <= 0x7ff) {
      byte += 2;
    } else if (2048 <= charCode && charCode <= 0xffff) {
      byte += 3;
    } else if (65536 < charCode && charCode <= 0x1FFFFF) {
      byte += 4;
    } else if (0x200000 < charCode && charCode <= 0x3FFFFFF) {
      byte += 5;
    } else if (0x4000000 < charCode && charCode <= 0x7FFFFFFF) {
      byte += 6;
    }
    // 当加上当前字符以后,如果总字节长度小于等于L,则将当前字符真实的+在result后
    if (byte <= L) {
      result += chr;
    } else {
      return result;
    }
  }
}

应用demo

<textarea></textarea>
<div class="number"></div>
<script type="text/javascript" src="js/jquery-3.2.1.js"></script>
<script type="text/javascript">
$(function(){
  //及时监听文本框内容的变化
  $("textarea").bind('input propertychange',function(){
    var str = $(this).val()
    var len = computeByteSize(str)
    if(len>256){
      $(this).val(cutStr(str,256))
    }else{
      $('.number').html(len)
    }
  });
})
</script>

但是目前截取字符串存在一些bug,如果直接复制一段中文内容到文本框,复制的内容超过限制的字节数,则会造成number的内容出错。而且如果输入的为中文,因为一个中文为3个字节数,一个拼音为一个字节数。所以也会出错。有可能造成最后的中文无法显示

注:汉字“?”(注意,这个字不是“吉祥”的“吉”)的码点是0x20BB7,UTF-16 编码为0xD842 0xDFB7(十进制为55362、57271),需要4个字节储存。对于这种4个字节的字符,JavaScript 不能正确处理,字符串长度会误判为2,而且charAt方法无法读取整个字符,charCodeAt方法只能分别返回前两个字节和后两个字节的值。因此ES6 提供了codePointAt方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。codePointAt 方法在第一个字符上,正确地识别了“?”,返回了它的十进制码点 134071(即十六进制的20BB7)

我的个人博客,有空来坐坐

上一篇下一篇

猜你喜欢

热点阅读