JavaScript拆分字符串时产生空字符的解决方案


Posted in Javascript onSeptember 26, 2014

问题描述

使用JavaScript的split方法拆分字符串时出现一些空字符串"",尤其是当使用正则表达式作为分隔符的时候。

相关问题

javascript正则表达式对字符串分组时产生空字符串组?

在上面这个问题中,题主使用正则表达式对字符串进行分割时产生了多个空字符串"",代码如下:

'张sdf四上法asdf翁芬aa33网s'.split(/([\u4e00-\u9fa5]{1})/gi);

//输出["", "张", "sdf", "四", "", "上", "", "法", "asdf", "翁", "", "芬", "aa33", "网", "s"]

 那么,产生这些空字符串的原因是什么?

问题分析

在Google上搜索了一番,发现相关的结果并不多,即便有,详细解释的也不多,大概的说了一下,然后就给出了一个ECMAScript规范的链接。看来要想知道真正的原因,就只能硬着头皮看规范了。

相关标准

那么,接下来,按照国际惯例,先上ECMAScript的标准镇楼。

String.prototype.split (separator, limit)

这个章节详细介绍了split方法的执行步骤,如果感兴趣的话可以一步一步的认真看完,我在这里只把和产生空字符串相关的步骤拿出来解释一下,不当之处,欢迎大家提出。

相关步骤

摘取部分步骤:

整个过程中最主要的步骤是第13步这个循环,而这个循环主要做的事情如下:
 •定义p, q的值,每一次循环开始的时候p和q的值是相同的(该步骤在循环之外);
 •调用SplitMatch(S, q, R)这个方法对字符串进行拆分;
 •根据返回结果的不同,执行不同的分支,主要分支为分支ⅲ;
 •分支ⅲ又分成了8个小步用来将返回的结果填充到事先定义好的数组A中
 •在这个8小步中,步骤1的作用是返回原始字符串的一个子串,开始位置是p(包含在内),结束位置是q(不包含在内),注意:在这一步中会产生空字符串,我将其标记为截取字符串,方便下文引用。
 •将上一步的子串添加到数组A中
 •接下来的几步是更新相关的变量,继续下一次循环。(步骤7的作用是将正则表达式中的捕获分组保存到数组A中,和产生空字符串无关)
 
SplitMatch(S, q, R)

接下来,我们需要了解一下SplitMatch(S, q, R)这个方法做了些什么事。这个方法在split规范中的下方有提及。它主要做的事是,根据分隔符(separator)的类型进行相应的操作:
 •如果分隔符是RegExp类型的,调用RegExp的内部方法[[Match]]来对字符串进行匹配,如果匹配失败,返回failure,否则,返回一个MatchResult类型的结果。
 •如果分隔符是字符串,进行匹配判断,失败返回failure,成功返回MatchResult类型的结果。
 
MatchResult

上面的步骤中又引出了一个MatchResult类型的变量。通过查文档发现,该类型的变量有两个属性endIndex和captures,endIndex的值是字符串匹配的位置加上1,captures可以理解为一个数组,当分隔符为正则表达式时,它里面的元素是分组捕获的值;当分隔符为字符串时,它为一个空数组。

接下来

我们从上面的步骤可以看出,分割的字符串是在截取字符串这一步骤中产生的(正则表达式的分组捕获除外)。它的作用是截取指定开始(包含在内)和结束位置(不包含在内)之间的字符串,那它什么时候会返回""呢?有一种特殊情况是开始位置和结束位置的值相等,这只是猜想而已,因为该规范没有给出截取字符串的规范步骤。

都走到这里了,为什么不再往前走一步呢?

于是,我试着搜索了一些V8的源码,看看能不能找到具体的实现方法。确实找到了相关的代码,源码链接

这里摘取其中一部分:

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;
    //分隔符是空字符串,直接返回了字符数组

    if (separator_length === 0) return %StringToArray(subject, limit);
    var result = %StringSplit(subject, separator_string, limit);
    return result;

  }
  if (limit === 0) return [];
  // 分隔符是正则表达式的情况,调用StringSplitOnRegExp

  return StringSplitOnRegExp(subject, separator, limit, length);

}
//此处省略若干代码

 我在代码中发现,在填充数组的时候会调用%_SubString这个方法来截取字符串,可惜的是我没有找到他的相关定义,如果有找到的同学欢迎告知。但是,我发现JavaScript中substring这个方法所对应的StringSubstring这个方法会调用%_SubString这个方法,并将其结果返回。那么如果'abc'.substring(1,1)返回"",则表明%_SubString这个方法在开始位置和结束位置相同的时候会返回"",结果大家一试便知。

那么,什么时候会出现开始位置等于结束位置(即q === p)的情况呢?我按照上面的步骤一步一步的进行分析,最终发现:
 •当原始字符串S匹配过一次分隔符之后,紧接着,字符串S的下一个位置还匹配分隔符。如:'abbbc'.split('b'),'abbbc'.split(/(b){1}/)
 •另一种情况是字符串开头的一个或几个字符匹配分隔符。如:'abc'.split('a'),'abc'.split(/ab/)
•还有一种情况是字符串结尾的一个或几个字符串匹配分隔符,与之相关的步骤是第14步。
 如:'abc'.split('c'),'abc'.split(/bc/)

此外,当使用正则表达式作为分隔符的时候,返回的结果中还有可能出现undefined。
 如:'abc'.split(/(d)*/)

回过头来再看看开头的那个例子,是不是满足上面几种情况?

题外话

这是我第一次这么仔细的看ECMAScript的标准规范,看的过程确实很痛苦,但明白之后就感觉很痛快了。也感谢题主提出的这个问题,以及追问。
 顺便提一句,正则表达式作为分隔符时,global修饰符g是会被忽略的,这也算是一次额外的收获。

Javascript 相关文章推荐
js实现全屏漂浮广告移入光标停止移动
Dec 02 Javascript
jQuery分组选择器用法实例
Dec 23 Javascript
jquery ui dialog替代confirm实例分析
Jan 25 Javascript
基于jQuery实现仿搜狐辩论投票动画代码(附源码下载)
Feb 18 Javascript
AngularJS入门教程之模块化操作用法示例
Nov 02 Javascript
从零学习node.js之搭建http服务器(二)
Feb 21 Javascript
微信小程序 弹窗自定义实例代码
Mar 08 Javascript
使用 Vue.js 仿百度搜索框的实例代码
May 09 Javascript
详解探索 vuex 2.0 以及使用 vuejs 2.0 + vuex 2.0 构建记事本应用
Jun 16 Javascript
详解Angular之constructor和ngOnInit差异及适用场景
Jun 22 Javascript
在微信小程序中使用图表的方法示例
Apr 25 Javascript
原生JS实现音乐播放器的示例代码
Feb 25 Javascript
JavaScript实现在数组中查找不同顺序排列的字符串
Sep 26 #Javascript
前端轻量级MVC框架CanJS详解
Sep 26 #Javascript
alert出数组中的随即值代码
Sep 25 #Javascript
jquery得到iframe src属性值的方法
Sep 25 #Javascript
jquery获得同源iframe内body下标签的值的方法
Sep 25 #Javascript
jquery 实现两Select 标签项互调示例代码
Sep 25 #Javascript
$("").click与onclick的区别示例介绍
Sep 25 #Javascript
You might like
PHP的5个安全措施小结
2012/07/17 PHP
sae使用smarty模板的方法
2013/12/17 PHP
PHP实现长文章分页实例代码(附源码)
2016/02/03 PHP
php实现当前页面点击下载文件的实例代码
2016/11/16 PHP
php微信分享到朋友圈、QQ、朋友、微博
2019/02/18 PHP
jquery.messager.js插件导致页面抖动的解决方法
2013/07/14 Javascript
一个简单的jQuery计算器实现了连续计算功能
2014/07/21 Javascript
Javascript冒泡排序算法详解
2014/12/03 Javascript
javascript实时显示当天日期的方法
2015/05/20 Javascript
jQuery+CSS3+Html5实现弹出层效果实例代码(附源码下载)
2016/05/16 Javascript
浅谈javascript中的三种弹窗
2016/10/21 Javascript
BootStrap Table 设置height表头与内容无法对齐的问题
2016/12/28 Javascript
详解Jquery Easyui的验证扩展
2017/01/09 Javascript
vue props传值失败 输出undefined的解决方法
2018/09/11 Javascript
原生js实现each方法实例代码详解
2019/05/27 Javascript
Vue替代marquee标签超出宽度文字横向滚动效果
2019/12/09 Javascript
ES6 async、await的基本使用方法示例
2020/06/06 Javascript
Python中函数的用法实例教程
2014/09/08 Python
Python实现的简单hangman游戏实例
2015/06/28 Python
在Python的while循环中使用else以及循环嵌套的用法
2015/10/14 Python
Python3 加密(hashlib和hmac)模块的实现
2017/11/23 Python
Python使用Scrapy保存控制台信息到文本解析
2017/12/27 Python
Python中文件的读取和写入操作
2018/04/27 Python
用python简单实现mysql数据同步到ElasticSearch的教程
2018/05/30 Python
django之静态文件 django 2.0 在网页中显示图片的例子
2019/07/28 Python
pytorch使用指定GPU训练的实例
2019/08/19 Python
Python英文文章词频统计(14份剑桥真题词频统计)
2019/10/13 Python
Mistine官方海外旗舰店:泰国国民彩妆品牌
2016/12/28 全球购物
Sixt美国租车:高端豪华车型自驾体验
2017/09/02 全球购物
美国运动鞋和服装网上商店:YCMC
2018/09/15 全球购物
英语自荐信常用语句
2013/12/13 职场文书
借款协议书
2014/09/16 职场文书
行政处罚听证告知书
2015/07/01 职场文书
大学生暑假实习总结
2015/07/13 职场文书
吉利入股戴姆勒后smart“长大了”
2022/04/21 数码科技
mysql 排序失效
2022/05/20 MySQL