JS字符串截取出現(xiàn)的bug以及解決方式
前言
在js中我們對字符串進行一部分截取,可以使用slice()
函數截取,也可以直接用substring()
函數來截取,但是截取也有可能出bug
const str='小??和小??今天吃了50塊錢的KFC' console.log(str.slice(0,5));
可以在控制臺看到,本來應該截取的字符串是'小??和小??'才對,卻少了一個字,這是什么原因呢?
js的字符編碼
在很早的時候,js使用的編碼規(guī)范是16位的字符編碼(USC-2),規(guī)定了每一個字對應16位的空間,16位的空間稱為碼元,字符串的所有屬性和方法(像是 length 屬性和 chatAt 方法)都是基于 16 位的碼元,但是后來生僻字越來越多,16位的空間不夠用了
就把編碼方式換成了utf-16,utf-16允許一個文字占用16位的空間也就是一個碼元或者32位的空間就是兩個碼元,一些特殊的文字就占用了兩個碼元,像'??'和'??'就占用了兩個碼元
使用碼元截取的bug
我們使用的length屬性實際上數的是碼元的數量,而使用slice()方法截取字符串是根據下標來截取的,下標也是指的碼元的下標
比如我們截取'小??'這兩個字,將slice()截取的范圍改為0到1也就是console.log(str.slice(0,2))
, '??'占用了兩個碼元,slice()只截取到了它第一個碼元的值,一個碼元形不成文字,這樣得到的就不是一個完整的字,而是一個亂碼了
使用碼點來正確截取字符串
既然使用碼元獲取不到正確的字符,那就可以使用碼點來截取了,什么是碼點呢?碼點不管你占用多少空間,一個文字就占一個碼點,一個碼點對應一個碼元或者兩個碼元,使用碼點截取就要寫一個截取的函數了
我們在字符串的原型對象上新建一個函數,傳入一個截取的起始坐標和結束坐標,準備好一個result變量存儲最終截取到的結果,和兩個代表碼元和碼點指針的變量
String.prototype.strSlice=function(sStart,sEnd){//截取的起始坐標和結束坐標 let result='' //截取的結果 let dIndex=0 //碼點的指針 let yIndex=0 //碼元的指針 }
接下來就要不斷地向右運行碼點和碼元的指針進行截取,所以需要一個無限循環(huán),當碼點的指針到達了結束的位置或者碼元的指針超出了數組的長度就結束循環(huán)返回最終截取的結果
while(1){ if(dIndex>=sEnd || yIndex>=this.length){ //結束循環(huán)條件 break; } //截取操作 } return result //返回截取結果
每一次循環(huán)就碼點和碼元移動一次指針,碼點直接每次移動1位,但是一個字符會存在兩個碼元,這樣碼元和碼點就對應不上了,需要根據字符占據的碼元數量來移動
在ES6為我們提供了一個函數codePointAt
可以得到碼點的值,碼點的值有可能是16位或者32位的,而一個文字占用16位,如果碼點的值超過16位說明這個文字占用了兩個碼元,我們就可以通過碼點的值判斷碼元的指針應該移動1位或者2位
while(1){ if(dIndex>=sEnd || yIndex>=this.length){ //結束循環(huán)條件 break; } //截取操作 const point=this.codePointAt(yIndex) //獲取碼點的值 dIndex++ //碼點指針每次+1 yIndex+=point > 0xffff ? 2:1 //判斷碼點的值是否超過16位,超過占用2個碼元,指針+2,沒有+1 } return result //返回截取結果
碼點和碼元的指針移動已經同步了,對應在同一個文字上,然后就可以截取文字了。當碼點的指針大于等于起始坐標就把對應的文字取出來放在result里,不能通過 this[yIndex] 取值,不然還是取的碼元對應的值,得通過碼點對應的值取出來,在ES6里還提供了一個函數fromCodePoint
,按照碼點的值恢復這個文字,將文字加到result里就行了
String.prototype.strSlice=function(sStart,sEnd){//截取的起始坐標和結束坐標 let result='' //截取的結果 let dIndex=0 //碼點的指針 let yIndex=0 //碼元的指針 while(1){ if(dIndex>=sEnd || yIndex>=this.length){ //結束循環(huán)條件 break; } //截取操作 const point=this.codePointAt(yIndex) //獲取碼點的值 if(dIndex>=sStart){ result+=String.fromCodePoint(point) } dIndex++ yIndex+=point > 0xffff ? 2:1 //判斷碼點的值是否超過16位,超過占用2個碼元,指針+2,沒有+1 } return result //返回截取結果 }
最后調用strSlice方法,傳入截取的起始坐標和結束坐標,截取到的結果也是我們想要的
console.log('截取的結果為:',str.strSlice(0,5));
總結
到此這篇關于JS字符串截取出現(xiàn)的bug以及解決方式的文章就介紹到這了,更多相關JS字符串截取bug內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
javascript實現(xiàn)簡約的頁面右下角點擊彈出窗口示例【測試可用】
這篇文章主要介紹了javascript實現(xiàn)的頁面右下角點擊彈出窗口功能,結合實例形式詳細分析了javascript頁面右下角點擊彈出窗口功能的相關步驟、原理與注意事項,需要的朋友可以參考下2023-07-07為什么JavaScript中0.1 + 0.2 != 0.3
這篇文章主要給大家介紹了關于為什么JavaScript中0.1 + 0.2 != 0.3的相關資料,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2020-12-12js HTML5 Ajax實現(xiàn)文件上傳進度條功能
這篇文章主要介紹了javascript實現(xiàn)文件上傳進度條功能的相關資料啊,感興趣的朋友可以參考一下2016-02-02