最近做分发资源到短视频平台需求的时候,用户要求输入的长度不是按照个数来限制,而是用字节数来判断用户输入长度,比如输入1 占位长度应该是1,输入12,占位长度也应该是1,因为每个数字占用一个字节,两个字节占一个字符,符号同理,而输入汉字,占位长度应该是1,因为一个汉字是两个字节,一个字符 看看element-ui官网对字符长度统计原理的解释
也就是,"123发生口角电话".length,可以理解为是按照索引来统计,很明显这不能满足我的需求
那么这个提示文字就需要我们自己去实现,且支持用户输入的时候,实时统计这个输入的字节长度,并且超出我的限制时候,我能对输入超长的字符串进行截取,而且我的需求是多渠道多资源分发,也就是一个资源,可以同时被多个渠道分发,一个渠道可以分发多个资源,那么除了相同渠道的限制数量是一致的以外,每个资源的已经有值的字节数也是不相同的,做好的效果如下
对已有超长的值进行截取,遗弃超出部分
用户输入按照字节数统计
实现步骤:
1.根据字节数来对字符进行截取demo(substring实现超长字符串截取)
<script>
//字符串转字节数组
function stringToByte(str) {
var bytes = new Array();
var len, c;
len = str.length;
for (var i = 0; i < len; i++) {
c = str.charCodeAt(i);
if (c >= 0x010000 && c <= 0x10FFFF) {
bytes.push(((c >> 18) & 0x07) | 0xF0);
bytes.push(((c >> 12) & 0x3F) | 0x80);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000800 && c <= 0x00FFFF) {
bytes.push(((c >> 12) & 0x0F) | 0xE0);
bytes.push(((c >> 6) & 0x3F) | 0x80);
bytes.push((c & 0x3F) | 0x80);
} else if (c >= 0x000080 && c <= 0x0007FF) {
bytes.push(((c >> 6) & 0x1F) | 0xC0);
bytes.push((c & 0x3F) | 0x80);
} else {
bytes.push(c & 0xFF);
}
}
return bytes;
}
//字节数组转字符串(处理了乱码)
function utf8ByteToUnicodeStr(utf8Bytes) {
var unicodeStr = "";
for (var pos = 0; pos < utf8Bytes.length;) {
var flag = utf8Bytes[pos];
var unicode = 0;
if ((flag >>> 7) === 0) {
unicodeStr += String.fromCharCode(utf8Bytes[pos]);
pos += 1;
} else if ((flag & 0xFC) === 0xFC) {
unicode = (utf8Bytes[pos] & 0x3) << 30;
unicode |= (utf8Bytes[pos + 1] & 0x3F) << 24;
unicode |= (utf8Bytes[pos + 2] & 0x3F) << 18;
unicode |= (utf8Bytes[pos + 3] & 0x3F) << 12;
unicode |= (utf8Bytes[pos + 4] & 0x3F) << 6;
unicode |= (utf8Bytes[pos + 5] & 0x3F);
unicodeStr += String.fromCharCode(unicode);
pos += 6;
} else if ((flag & 0xF8) === 0xF8) {
unicode = (utf8Bytes[pos] & 0x7) << 24;
unicode |= (utf8Bytes[pos + 1] & 0x3F) << 18;
unicode |= (utf8Bytes[pos + 2] & 0x3F) << 12;
unicode |= (utf8Bytes[pos + 3] & 0x3F) << 6;
unicode |= (utf8Bytes[pos + 4] & 0x3F);
unicodeStr += String.fromCharCode(unicode);
pos += 5;
} else if ((flag & 0xF0) === 0xF0) {
unicode = (utf8Bytes[pos] & 0xF) << 18;
unicode |= (utf8Bytes[pos + 1] & 0x3F) << 12;
unicode |= (utf8Bytes[pos + 2] & 0x3F) << 6;
unicode |= (utf8Bytes[pos + 3] & 0x3F);
unicodeStr += String.fromCharCode(unicode);
pos += 4;
} else if ((flag & 0xE0) === 0xE0) {
unicode = (utf8Bytes[pos] & 0x1F) << 12;;
unicode |= (utf8Bytes[pos + 1] & 0x3F) << 6;
unicode |= (utf8Bytes[pos + 2] & 0x3F);
unicodeStr += String.fromCharCode(unicode);
pos += 3;
} else if ((flag & 0xC0) === 0xC0) { //110
unicode = (utf8Bytes[pos] & 0x3F) << 6;
unicode |= (utf8Bytes[pos + 1] & 0x3F);
unicodeStr += String.fromCharCode(unicode);
pos += 2;
} else {
unicodeStr += String.fromCharCode(utf8Bytes[pos]);
pos += 1;
}
}
return unicodeStr;
}
//获取汉字的个数
function getHanziNum(pastr) {
var m = {};
var psArr = pastr.split("");
var reg = new RegExp("[\u4E00-\u9FA5]+");
var realindex = 0;
psArr.forEach(item => {
if (reg.test(item)) {
m[realindex] = true;
realindex++;
m[realindex] = true;
} else {
m[realindex] = false;
}
realindex++;
});
return m;
}
//核心方法,截取str前len个字节并转换为字符串
function getNlenghtStr(str, len) {
//统计每个字节是否为汉字的字节数组
var targetObj = getHanziNum(str);
// 目标字符串转为字节数组
var pa = stringToByte(str);
//确定实际截取字节长度
let keys = Object.keys(targetObj);
var subArr = keys.slice(0, len);
//统计有多少个汉字
var hanziNum = 0;
for (var i = 0; i < subArr.length; i++) {
if (targetObj[i]) {
hanziNum++;
}
}
if(hanziNum % 2 == 1){
hanziNum--;
len--;
}
var realLenth = len + parseInt(hanziNum / 2);
//截取前21个字节,期望得到的 1思23路4327串56的每个
var spa = pa.slice(0, realLenth);
var newString = utf8ByteToUnicodeStr(spa);
return newString;
}
//测试demo
var str = "1思23路4327串56的每个字符进行二进制的位移处理";
var result = getNlenghtStr(str, 400);
alert(result);
</script>
2.用户实时输入统计已占字符数
handletextInput(val,item,values){//给输入框绑定@input事件
let len =0 //当前已经占用的字符长度
let limitwords;//判断当前渠道的字符数范围
for(let i=0;i<val.length;i++){
if(val.charCodeAt(i) < 0 || val.charCodeAt(i) > 255){
len += 1
}else{
len +=0.5;
}
}
// 判断当前是那种渠道,确定最大输入限制数
switch (item.type) {
case 5://抖音
limitwords = 500
break;
case 7://百家号
limitwords = 30
break;
case 8://网易号
limitwords = 30
break;
case 9://头条
limitwords = 30
break;
case 10://快手
limitwords = 500
break;
}
let valLength
if(len<=30){
valLength = Math.ceil(len);不满足一个字符,向上取整占位一个字符
} else {
if([7,8,9].includes(item.type)){
valLength = 30
} else if([5,10].includes(item.type)){
valLength = 500
}
}
// 如果对应渠道输入的值超出限制,则对用户输入的值进行裁剪
if([7,8,9].includes(item.type) && valLength==30){
values.value = this.getNlenghtStr(values.value, 60)//30个字符限制,也就是60个字节
} else if([5,10].includes(item.type) && valLength==500){//500个字的限制也就是1000个字节数
values.value = this.getNlenghtStr(values.value, 1000)
}
this.$nextTick(_=>{//统计之后给每个资源对象更新页面限制数量dom内容
this.$set(values,'valueLengths',`${valLength}/${limitwords}`)
this.$set(values,'valueLength',valLength)
})
},