JavaScript拆分字符串時產(chǎn)生空字符的解決方案
問題描述
使用JavaScript的split方法拆分字符串時出現(xiàn)一些空字符串"",尤其是當使用正則表達式作為分隔符的時候。
相關(guān)問題
javascript正則表達式對字符串分組時產(chǎn)生空字符串組?
在上面這個問題中,題主使用正則表達式對字符串進行分割時產(chǎn)生了多個空字符串"",代碼如下:
'張sdf四上法asdf翁芬aa33網(wǎng)s'.split(/([\u4e00-\u9fa5]{1})/gi);
//輸出["", "張", "sdf", "四", "", "上", "", "法", "asdf", "翁", "", "芬", "aa33", "網(wǎng)", "s"]
那么,產(chǎn)生這些空字符串的原因是什么?
問題分析
在Google上搜索了一番,發(fā)現(xiàn)相關(guān)的結(jié)果并不多,即便有,詳細解釋的也不多,大概的說了一下,然后就給出了一個ECMAScript規(guī)范的鏈接。看來要想知道真正的原因,就只能硬著頭皮看規(guī)范了。
相關(guān)標準
那么,接下來,按照國際慣例,先上ECMAScript的標準鎮(zhèn)樓。
String.prototype.split (separator, limit)
這個章節(jié)詳細介紹了split方法的執(zhí)行步驟,如果感興趣的話可以一步一步的認真看完,我在這里只把和產(chǎn)生空字符串相關(guān)的步驟拿出來解釋一下,不當之處,歡迎大家提出。
相關(guān)步驟
摘取部分步驟:
整個過程中最主要的步驟是第13步這個循環(huán),而這個循環(huán)主要做的事情如下:
•定義p, q的值,每一次循環(huán)開始的時候p和q的值是相同的(該步驟在循環(huán)之外);
•調(diào)用SplitMatch(S, q, R)這個方法對字符串進行拆分;
•根據(jù)返回結(jié)果的不同,執(zhí)行不同的分支,主要分支為分支ⅲ;
•分支ⅲ又分成了8個小步用來將返回的結(jié)果填充到事先定義好的數(shù)組A中
•在這個8小步中,步驟1的作用是返回原始字符串的一個子串,開始位置是p(包含在內(nèi)),結(jié)束位置是q(不包含在內(nèi)),注意:在這一步中會產(chǎn)生空字符串,我將其標記為截取字符串,方便下文引用。
•將上一步的子串添加到數(shù)組A中
•接下來的幾步是更新相關(guān)的變量,繼續(xù)下一次循環(huán)。(步驟7的作用是將正則表達式中的捕獲分組保存到數(shù)組A中,和產(chǎn)生空字符串無關(guān))
SplitMatch(S, q, R)
接下來,我們需要了解一下SplitMatch(S, q, R)這個方法做了些什么事。這個方法在split規(guī)范中的下方有提及。它主要做的事是,根據(jù)分隔符(separator)的類型進行相應(yīng)的操作:
•如果分隔符是RegExp類型的,調(diào)用RegExp的內(nèi)部方法[[Match]]來對字符串進行匹配,如果匹配失敗,返回failure,否則,返回一個MatchResult類型的結(jié)果。
•如果分隔符是字符串,進行匹配判斷,失敗返回failure,成功返回MatchResult類型的結(jié)果。
MatchResult
上面的步驟中又引出了一個MatchResult類型的變量。通過查文檔發(fā)現(xiàn),該類型的變量有兩個屬性endIndex和captures,endIndex的值是字符串匹配的位置加上1,captures可以理解為一個數(shù)組,當分隔符為正則表達式時,它里面的元素是分組捕獲的值;當分隔符為字符串時,它為一個空數(shù)組。
接下來
我們從上面的步驟可以看出,分割的字符串是在截取字符串這一步驟中產(chǎn)生的(正則表達式的分組捕獲除外)。它的作用是截取指定開始(包含在內(nèi))和結(jié)束位置(不包含在內(nèi))之間的字符串,那它什么時候會返回""呢?有一種特殊情況是開始位置和結(jié)束位置的值相等,這只是猜想而已,因為該規(guī)范沒有給出截取字符串的規(guī)范步驟。
都走到這里了,為什么不再往前走一步呢?
于是,我試著搜索了一些V8的源碼,看看能不能找到具體的實現(xiàn)方法。確實找到了相關(guān)的代碼,源碼鏈接
這里摘取其中一部分:
function StringSplitJS(separator, limit) {
...
...
//分隔符是字符串的情況
if (!IS_REGEXP(separator)) {
var separator_string = TO_STRING_INLINE(separator);
if (limit === 0) return [];
// ECMA-262 says that if separator is undefined, the result should
// be an array of size 1 containing the entire string.
if (IS_UNDEFINED(separator)) return [subject];
var separator_length = separator_string.length;
//分隔符是空字符串,直接返回了字符數(shù)組
if (separator_length === 0) return %StringToArray(subject, limit);
var result = %StringSplit(subject, separator_string, limit);
return result;
}
if (limit === 0) return [];
// 分隔符是正則表達式的情況,調(diào)用StringSplitOnRegExp
return StringSplitOnRegExp(subject, separator, limit, length);
}
//此處省略若干代碼
我在代碼中發(fā)現(xiàn),在填充數(shù)組的時候會調(diào)用%_SubString這個方法來截取字符串,可惜的是我沒有找到他的相關(guān)定義,如果有找到的同學(xué)歡迎告知。但是,我發(fā)現(xiàn)JavaScript中substring這個方法所對應(yīng)的StringSubstring這個方法會調(diào)用%_SubString這個方法,并將其結(jié)果返回。那么如果'abc'.substring(1,1)返回"",則表明%_SubString這個方法在開始位置和結(jié)束位置相同的時候會返回"",結(jié)果大家一試便知。
那么,什么時候會出現(xiàn)開始位置等于結(jié)束位置(即q === p)的情況呢?我按照上面的步驟一步一步的進行分析,最終發(fā)現(xiàn):
•當原始字符串S匹配過一次分隔符之后,緊接著,字符串S的下一個位置還匹配分隔符。如:'abbbc'.split('b'),'abbbc'.split(/(b){1}/)
•另一種情況是字符串開頭的一個或幾個字符匹配分隔符。如:'abc'.split('a'),'abc'.split(/ab/)
•還有一種情況是字符串結(jié)尾的一個或幾個字符串匹配分隔符,與之相關(guān)的步驟是第14步。
如:'abc'.split('c'),'abc'.split(/bc/)
此外,當使用正則表達式作為分隔符的時候,返回的結(jié)果中還有可能出現(xiàn)undefined。
如:'abc'.split(/(d)*/)
回過頭來再看看開頭的那個例子,是不是滿足上面幾種情況?
題外話
這是我第一次這么仔細的看ECMAScript的標準規(guī)范,看的過程確實很痛苦,但明白之后就感覺很痛快了。也感謝題主提出的這個問題,以及追問。
順便提一句,正則表達式作為分隔符時,global修飾符g是會被忽略的,這也算是一次額外的收獲。
相關(guān)文章
JS基于FileSystemObject創(chuàng)建一個指定路徑的TXT文本文件
這篇文章主要介紹了JS基于FileSystemObject創(chuàng)建一個指定路徑的TXT文本文件,涉及javascript使用ActiveXObject控件中FileSystemObject對象模型的基本技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-08-08原生js如何實現(xiàn)call,apply以及bind
這篇文章主要介紹了原生js實現(xiàn)call,apply以及bind,幫助大家更好的理解和學(xué)習使用JavaScript,感興趣的朋友可以了解下2021-04-04利用chrome瀏覽器進行js調(diào)試并找出元素綁定的點擊事件詳解
“工欲善其事,必先利其器” 恩,這句話我覺得說的特別有道理,下面這篇文章主要給大家介紹了關(guān)于利用chrome瀏覽器進行js調(diào)試并找出元素綁定的點擊事件的相關(guān)資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考下2018-09-09JS與Jquery獲取屏幕、瀏覽器、頁面的寬度和高度對比整理
頁面的展示,從外到內(nèi)的容器為:屏幕、瀏覽器以及頁面本身。HTML元素展現(xiàn)在頁面內(nèi),頁面展現(xiàn)在瀏覽器內(nèi),而瀏覽器展現(xiàn)在屏幕內(nèi)。通過Js的一些對象可以獲取這些容器的高度、寬度。容器的尺寸是指當前分辨率下的高度和寬度(分辨率不同,值就不相同)。2023-02-02Bootstrap中的表單驗證插件bootstrapValidator使用方法整理(推薦)
這篇文章主要介紹了Bootstrap中的表單驗證插件bootstrapValidator使用方法整理(推薦)的相關(guān)資料,非常不錯,具有參考借鑒價值,需要的朋友可以參考下2016-06-06TS報錯:Parameter?'xxx'?implicitly?has?an?'
這篇文章主要給大家介紹了關(guān)于TS報錯:Parameter?'xxx'?implicitly?has?an?'any'?type的解決方式,文中將產(chǎn)生錯誤的原因及解決方法都介紹的非常詳細,需要的朋友可以參考下2022-10-10JavaScript去掉數(shù)組重復(fù)項的方法分析【測試可用】
這篇文章主要介紹了JavaScript去掉數(shù)組重復(fù)項的方法,結(jié)合實例形式分析了javascript使用object特性實現(xiàn)數(shù)組去除重復(fù)項功能的相關(guān)操作技巧,需要的朋友可以參考下2018-07-07Javascript獲取數(shù)組中的最大值和最小值的方法匯總
比較數(shù)組中數(shù)值的大小是比較常見的操作,下面同本文給大家分享四種放哪廣發(fā)獲取數(shù)組中最大值和最小值,對此感興趣的朋友一起學(xué)習吧2016-01-01