BOM 和 JavaScript 中的 trim

标签: bom javascript trim | 发表时间:2014-03-29 07:59 | 作者:
出处:https://www.imququ.com

今天遇到一个 IE7 下 JSON.parse 失败的问题。经过排查发现:服务端某个配置文件编码是 UTF-8 + BOM,输出的字符串最开始包含了 BOM 字符,不是合法的 JSON。

IE7 不支持原生 JSON,我们项目中使用的是 json2.js,但解析不了开头有 BOM 字符的 JSON 不是 json2 的错,其他浏览器正常是因为它们忽略了响应正文开头的 BOM。如果像下面这样写,每个浏览器都会抛异常:

  

把这段代码贴进无敌强大的 CodeMirror,就能轻松发现这个不可见的 BOM 字符:

codemirror invalidchar

BOM 引发的问题多得数不胜数,网上相关记载也很多,这里不再啰嗦了。就今天遇到的情况来看,虽然问题出在接口提供方,但考虑到代码健壮性,也可以先 trim 字符串,再 JSON.parse。

String.prototype.trim 是 ES5 增加的方法,对于老旧浏览器,还得使用自己实现的 trim。我们先来看看 QWrap 和 jQuery 关于 trim 的实现:

  //jQuery 1.7.2:
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
return text == null ? "" : text.toString()
                               .replace( trimLeft, "" )
                               .replace( trimRight, "" );

jQuery 1.7.2 会过滤字符串两端的 \s\xA0。对于IE 低版本, \s 等价于 [ \t\v\f\r\n]。这些字符的含义如下表:

名称 Unicode 编码 字符串表示 说明
U+0020 " ", "\x20", "\u0020" 半角空格符,键盘空格键
U+0009 "\t", "\x09", "\u0009" 制表符,键盘 tab 键
U+000B "\v", "\x0B", "\u000B" 垂直制表符
U+000C "\f", "\x0C", "\u000C" 换页符
U+000D "\r", "\x0D", "\u000D" 回车符
U+000A "\n", "\x0A", "\u000A" 换行符
U+00A0 "\xA0", "\u00A0" No-Break Space
禁止自动换行空格符

最后的「禁止自动换行空格符」 ,实际上就是 HTML 中经常用到的  。HTML 中,连续空白字符(半角空格、换行、tab 等)会被合并为一个空格,   则不会与其它相邻的空白字符合并。

可以看到,至少在 IE 低版本下,jQuery 1.7.2 无法过滤字符串两端的 BOM 字符。

  //jQuery 1.8.1
rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
return text == null ?
                "" :
                text.toString().replace( rtrim, "" );

jQuery 1.8.1 在之前的基础上,又增加了 \uFEFF。它是 ES5 新增的空白符,叫「字节次序标记字符(Byte Order Mark)」,也就是前面提到的 BOM

名称 Unicode 编码 字符串表示
U+FEFF "\uFEFF"

Unicode3.2 之前, \uFEFF 表示「零宽不换行空格(Zero Width No-Break Space)」;Unicode3.2 新增了 \u2060 用来表示零宽不换行空格, \uFEFF 就只用来表示字节次序标记了。

可以看到,jQuery 1.8.1 可以过滤 BOM。另外,考虑到某些浏览器实现的 trim 不过滤 ,jQuery 加了一层检测,并不是存在原生 trim 就一定用原生的。

  //QWrap 1.1.6
return s.replace(/^[\s\uFEFF\xa0\u3000]+|[\uFEFF\xa0\u3000\s]+$/g, "");

QWrap 的 trim,还增加了 \u3000。它是「表意文字空格(IDEOGRAPHIC SPACE)」,用于 CJK 统一表意文字,可以简单地认为它就是平时我们经常遇到的中文全角空格。

Unicode 编码 字符串表示 说明
U+3000 " ", "\u3000" IDEOGRAPHIC SPACE,CJK 全角空格

再来看看浏览器的 trim 应该处理哪些字符,ES5 文档中这样描述:

Let T be a String value that is a copy of S with both leading and trailing white space removed. The definition of white space is the union of WhiteSpace and LineTerminator.

也就是说字符串两端的 WhiteSpaceLineTerminator 都要去掉。

ES5 文档规定的 WhiteSpace,除了包含上面提过的 之外,还包含 定义的其它空白字符,USP 表示 Unicode 中「Separator, Space」分类下的字符,具体内容如下(来源: 12):

Unicode 编码 说明
U+0020 SPACE,
U+00A0 NO-BREAK SPACE,
U+1680 OGHAM SPACE MARK,欧甘空格
U+180E Mongolian Vowel Separator,蒙古文元音分隔符
U+2000 EN QUAD
U+2001 EM QUAD
U+2002 EN SPACE,En 空格。与 en 同宽(em 的一半)
U+2003 EM SPACE,Em 空格。与 em 同宽
U+2004 THREE-PER-EM SPACE,Em 三分之一空格
U+2005 FOUR-PER-EM SPACE,Em 四分之一空格
U+2006 SIX-PER-EM SPACE,Em 六分之一空格
U+2007 FIGURE SPACE,数字空格。与单一数字同宽
U+2008 PUNCTUATION SPACE,标点空格。与同字体窄标点同宽
U+2009 THIN SPACE,窄空格。em 六分之一或五分之一宽
U+200A HAIR SPACE,更窄空格。比窄空格更窄
U+200B Zero Width Space,,零宽空格
U+200C Zero Width Non Joiner,,零宽不连字空格
U+200D Zero Width Joiner,,零宽连字空格
U+202F NARROW NO-BREAK SPACE,窄式不换行空格
U+205F MEDIUM MATHEMATICAL SPACE,中数学空格。
用于数学方程式
U+2060 Word Joiner,同 U+200B,但该处不换行。
Unicode3.2 新增,代替 U+FEFF
U+3000 IDEOGRAPHIC SPACE,表意文字空格。即全角空格
U+FEFF Byte Order Mark,,字节次序标记字符。
不换行功能于 Unicode3.2 起废止

再来看看文档对 LineTerminator 的定义,除了前面介绍过的 (Line Feed,换行符)和 (Carriage Return,回车符),还有两个:

名称 Unicode 编码 说明
U+2028 行分隔符
U+2029 段落分隔符

可以看到,ES5 定义的 trim 方法十分强大。浏览器实现方面,我测试了 Chrome 和 Firefox,上述大部分不可见字符都能被过滤,也能被正则里的 \s 匹配到。

QWrap 或 jQuery 实现的 trim,则只处理了常见字符,通常也够用。如果需要与 ES5 更加一致的 trim,可以看看 es5-shim 这个项目:

  var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF";
if (!String.prototype.trim || ws.trim()) {
    // http://blog.stevenlevithan.com/archives/faster-trim-javascript
    // http://perfectionkills.com/whitespace-deviations/
    ws = "[" + ws + "]";
    var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
        trimEndRegexp = new RegExp(ws + ws + "*$");
    String.prototype.trim = function trim() {
        if (this === void 0 || this === null) {
            throw new TypeError("can't convert "+this+" to object");
        }
        return String(this)
            .replace(trimBeginRegexp, "")
            .replace(trimEndRegexp, "");
    };
}

本文链接: https://www.imququ.com/post/bom-and-javascript-trim.html

--EOF--

推荐: 领略前端技术 阅读奇舞周刊

相关 [bom javascript trim] 推荐:

BOM 和 JavaScript 中的 trim

- - JerryQu 的小站
今天遇到一个 IE7 下 JSON.parse 失败的问题. 经过排查发现:服务端某个配置文件编码是 UTF-8 + BOM,输出的字符串最开始包含了 BOM 字符,不是合法的 JSON. IE7 不支持原生 JSON,我们项目中使用的是 json2.js,但解析不了开头有 BOM 字符的 JSON 不是 json2 的错,其他浏览器正常是因为它们忽略了响应正文开头的 BOM.

关于BOM模型

- - CSDN博客编程语言推荐文章
当我们使用浏览器打开一个网页程序时,那么,js系统会自动创建对象,首先创建浏览器对象window,然后再为window对象创建它的子级对象,最后形成一个树状模型,这个就是BOM模型. BOM定义了JavaScript可以进行操作的浏览器的各个功能部件的接口. BOM 主要处理浏览器窗口和框架,不过通常浏览器特定的 JavaScript 扩展都被看做 BOM 的一部分.

UTF-8编码中BOM的检测与删除

- 競 - 火丁笔记
所谓BOM,全称是Byte Order Mark,它是一个Unicode字符,通常出现在文本的开头,用来标识字节序(Big/Little Endian),除此以外还可以标识编码(UTF-8/16/32),如果出现在文本中间,则解释为zero width no-break space. 注:Unicode相关知识的详细介绍请参考UTF-8, UTF-16, UTF-32 & BOM.

JSP, C 写入文件添加BOM头实例

- - CSDN博客推荐文章
注: 以下的状况不加BOM头也不会有中文乱码问题, 只是演示一下JSP输出文件如何加BOM头. out.write()是字节输出流的方法. out.print()是字符输出流的方法. 作者:oscar999 发表于2012-1-10 10:41:44 原文链接. 阅读:3 评论:0 查看评论.

java UTF-8 和 UTF-8 without BOM工具处理类

- - CSDN博客推荐文章
作者:shefron 发表于2014-8-27 15:52:22 原文链接. 阅读:6 评论:0 查看评论.

Javascript诞生记

- Milido - 阮一峰的网络日志
二周前,我谈了一点Javascript的历史. 今天把这部分补全,从历史的角度,说明Javascript到底是如何设计出来的. 只有了解这段历史,才能明白Javascript为什么是现在的样子. 我依据的资料,主要是Brendan Eich的自述. "1994年,网景公司(Netscape)发布了Navigator浏览器0.9版.

JavaScript,你懂的

- dylan - keakon的涂鸦馆
经常有人问我,JavaScript应该怎么学. 先学基本语法,如果曾学过C等语言,应该1小时内就能掌握了. 再去使用内置的函数、方法和DOM API,熟悉它能干什么;而在学习DOM API的过程中,你还不得不与HTML和CSS打交道. 然后弄懂匿名函数和闭包,学会至少一个常用的JavaScript库(例如jQuery).

Javascript 里跑Linux

- rockmaple - Shellex's Blog
牛逼到暴的大拿 Fabrice Bellard,用Javascript实现了一个x86 PC 模拟器,然后成功在这个模拟器里面跑Linux(请用Firefox 4 / Google Chrome 11打开,Chome 12有BUG). 关于这个东西… 伊说 “I did it for fun“,大大啊大大啊….

高效 JavaScript

- xtps - ITeye论坛最新讨论
传统上,网页中不会有大量的脚本,至少脚本很少会影响网页的性能. 但随着网页越来越像 Web 应用程序,脚本的效率对网页性能影响越来越大. 而且使用 Web 技术开发的应用程序现在越来越多,因此提高脚本的性能变得很重要. 对于桌面应用程序,通常使用编译器将源代码转换为二进制程序. 编译器可以花费大量时间优化最终二进制程序的效率.

你得学JavaScript

- 蒋冰 - 伯乐在线 -博客
  注:本文由 敏捷翻译 - 蒋少雄 翻译自 Kenny Meyers 的博文.   如果三年前你问我应该学什么语言,我会告诉你是Ruby.   如果你现在想学一门语言的话,你应该学习JavaScript..   我认为,每一位Web开发人员都应该学习JavaScript. 目前推出的许多新技术都支持这个观点.