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 相关文章推荐
jquery tab标签页的制作
May 10 Javascript
JavaScript 设计模式 富有表现力的Javascript(一)
May 26 Javascript
基于datagrid框架的查询
Apr 08 Javascript
JS实现跟随鼠标立体翻转图片的方法
May 04 Javascript
JS中如何实现点击a标签返回页面顶部的问题
Jan 19 Javascript
JavaScript通过改变文字透明度实现的文字闪烁效果实例
Apr 27 Javascript
对vue.js中this.$emit的深入理解
Feb 23 Javascript
mpvue中配置vuex并持久化到本地Storage图文教程解析
Mar 15 Javascript
vue环形进度条组件实例应用
Oct 10 Javascript
微信小程序如何再次获取用户授权的方法
May 10 Javascript
Vue实现购物车实例代码两则
May 30 Javascript
antd-DatePicker组件获取时间值,及相关设置方式
Oct 27 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
印尼林东PWN黄金曼特宁咖啡豆:怎么冲世界上最醇厚的咖啡冲煮教程
2021/03/03 冲泡冲煮
php检测mysql表是否存在的方法小结
2017/07/20 PHP
php表单处理操作
2017/11/16 PHP
jQuery打印指定区域Html页面并自动分页
2014/07/04 Javascript
javascript ajax的5种状态介绍
2014/08/18 Javascript
javascript将url中的参数加密解密代码
2014/11/17 Javascript
js制作简易年历完整实例
2015/01/28 Javascript
js获取本机操作系统类型的两种方法
2015/12/19 Javascript
阿里云ecs服务器中安装部署node.js的步骤
2016/10/08 Javascript
浅谈bootstrap使用中的一些问题以及解决过程
2016/10/18 Javascript
BootStrap 下拉菜单点击之后不会出现下拉菜单(下拉菜单不弹出)的解决方案
2016/12/14 Javascript
node.js 发布订阅模式的实例
2017/09/10 Javascript
JS获取一个表单字段中多条数据并转化为json格式
2017/10/17 Javascript
nodejs超出最大的调用栈错误问题
2017/12/27 NodeJs
使用JavaScript生成罗马字符的实例代码
2018/06/08 Javascript
vue中render函数的使用详解
2018/10/12 Javascript
layui默认选中table的CheckBox复选框方法
2019/09/19 Javascript
Vue+ElementUI使用vue-pdf实现预览功能
2019/11/26 Javascript
微信小程序封装多张图片上传api代码实例
2019/12/30 Javascript
[03:11]DOTA2上海特锦赛小组赛第一日recap精彩回顾
2016/02/28 DOTA
python中的文件打开与关闭操作命令介绍
2018/04/26 Python
Python错误处理操作示例
2018/07/18 Python
Linux上使用Python统计每天的键盘输入次数
2019/04/17 Python
python替换字符串中的子串图文步骤
2019/06/19 Python
Django之提交表单与前后端交互的方法
2019/07/19 Python
python将三维数组展开成二维数组的实现
2019/11/30 Python
使用python实现微信小程序自动签到功能
2020/04/27 Python
Html5移动端获奖无缝滚动动画实现示例
2018/06/25 HTML / CSS
英国一家集合了众多有才华设计师品牌的奢侈店:Wolf & Badger
2018/04/18 全球购物
P D PAOLA法国官网:西班牙著名的珠宝首饰品牌
2020/02/15 全球购物
实习教师自我鉴定
2013/12/09 职场文书
教学评估实施方案
2014/03/16 职场文书
中秋节活动总结
2014/08/29 职场文书
初中政教处工作总结
2015/08/12 职场文书
浅谈golang 中time.After释放的问题
2021/05/05 Golang
Python上下文管理器Content Manager
2021/06/26 Python