原文:http://ariya.ofilabs.com/2012/02/from-double-quotes-to-single-quotes.html


代码的不一致性总是让人发狂,如果每位开发者都能遵守约定好的编码规范(coding conventions),那么生活将变的更加美好.比如在JavaScript中,一个字符串字面量可以用单引号引起,也可以用双引号来引起(ECMAScript 5规范7.8.4小节).很多人习惯于使用某种特定的引号,比如jQuery编码风格推荐人们使用双引号.

但我个人更喜欢使用单引号,这仅仅是我的偏好.自从有了Esprima,我意识到,我可以利用Esprima能够以非破坏式(non-destructive)的方式对输入的JavaScript源码进行局部修改(partial modification)的能力,来强制让输入JavaScript源码中的每个字符串字面量都使用单引号.于是我就写出了下面的singlequote.js脚本:

var fs = require('fs'),
esprima = require('esprima'),
input = process.argv[2],
output = process.argv[3],
offset = 0,
content = fs.readFileSync(input, 'utf-8'),
tokens = esprima.parse(content, { tokens: true, range: true }).tokens; function convert(literal) {
var result = literal.substring(1, literal.length - 1);
result = result.replace(/'/g, '\'');
return ''' + result + ''';
} tokens.forEach(function (token) {
var str;
if (token.type === 'String' && token.value[0] !== '\'') {
str = convert(token.value);
content = content.substring(0, offset + token.range[0]) + str +
content.substring(offset + token.range[1] + 1, content.length);
offset += (str.length - token.value.length);
}
});
fs.writeFileSync(output, content); 

这个脚本需要用Node.js来执行,像这样:

node singlequote.js inputfile outputfile

该脚本具体是如何工作的?让我们假设输入源码的内容是这样的:

console.log("Hello")

在我们把这句代码传入Esprima解析器的时候,要把其中一个解析选项tokens设为true,这样解析器才会在解析的过程中把遇到的所有token(词法单元)收集到一个数组中,并返回它.对于我们上面的这句代码,返回的token数组看起来是这样的:

[
{ type: "Identifier", value: "console", range: [0, 6] },
{ type: "Punctuator", value: ".", range: [7, 7] },
{ type: "Identifier", value: "log", range: [8, 10] },
{ type: "Punctuator", value: "(", range: [11, 11] },
{ type: "String", value: ""Hello"", range: [12, 18] },
{ type: "Punctuator", value: ")", range: [19, 19] }
]

译者注:在编译原理领域中,token这个词可以被翻译成词法单元或者词法记号,它表示一个在源码中拥有独立意义的最小单位,是不可再分的词法单元(lexical unit).一个token是由若干个字符组成的,就像英文中的单词一样,所以也有人直接把它翻译成单词.在ES标准中,token具体包含有保留字,标识符,字面量,标点符号这几种输入元素(input element).

一旦我们拿到了所有的token对象,剩下的事情就简单多了.我们只需要遍历这个token对象数组,找到那些与某个字符串字面量关联的token(type属性为String的token对象).每个token对象都会在自己的range属性中存放有所关联字符串字面量的位置信息,这是一个表示了所关联字符串字面量在输入源码中的开始位置和结束位置的索引区间数组(闭区间).

 { type: "String", value: ""Hello"", range: [12, 18] }

有了这个位置信息,我们就能使用一些基本的字符串操作来替换输入源码中的某段内容.对于上面这个例子的话,修改目标就是索引位置在[12, 18]之间的源码内容.值得注意的是,如果原始字符串字面量的值(两个引号中间夹着的内容)中包含有一个或多个的单引号,则我们需要做一些额外的工作,就是要把这些单引号进行转义(查看第7.8.4小节的SingleEscapeCharacters).如果真的需要进行这样的转义,则转义后的字符串字面量的长度会大于原始字符串字面量的长度(多了反斜杠字符),从而改变了整个源码的长度,再从而让token数组中其它还未处理的token对象中包含的位置信息产生错位,因此我们还需要进行偏移量的调整.下面是个需要进行转义的字符串字面量的例子:

// 输入源码
"color = 'blue'"; // 不进行转义操作的话会输出非法的字符串字面量
'color = 'blue'';

另外,我写的转换代码还差一件事情没做,就是要把那些不再需要的转义字符也删除掉.也就是原来字符串字面量中包含的双引号前面的那个反斜杠,它已经不再需要了.这项工作就留给读者们实现吧!

译者注:作者说的是这种情况,原字符串字面量为"\"",这时用反斜杠转义里面的双引号是必须的,但按照上面的规则转换之后就成了'\"',虽然这也是一个合法的字符串字面量,且求值结果不变.不过作者的意思是这个反斜杠是多余的,是应该删除掉的.

很显然,我写的这个工具只是为教学演示而用.另外,虽然现在大部分编辑器都支持搜索替换的功能,如果你需要使用编辑器来完成这项任务,也有一定的难度,注意不要替换掉那些不在字符串字面量两边的引号.

译者注:你觉的能用正则表达式来完成这项任务吗?注释和正则字面量中的引号字符会让你束手无策.

你还可以想想看,利用token列表和源码局部修改的技术,还能干哪些事情?

译者注:你有没有发现作者忽略了一个比较极端的情况,就是假如原字符串字面量为"\'",虽然这个反斜杠是多余的,但这的确是一个合法的字符串写法,求值后字符串的值为一个单引号.按照上面的算法转换之后会变成'\\''.显然,这会导致一个语法错误,因为少了一个反斜杠.是不是呢?

有了Esprima,实现这样一个转换器真的是很简单,如下

[译]JavaScript:将字符串两边的双引号转换成单引号的更多相关文章

  1. JavaScript清除空格、换行,把双引号转换成单引号

    1.页面 2.源码 <!DOCTYPE> <html> <head> <meta charset="utf-8"> <titl ...

  2. Javascript里,想把一个整数转换成字符串,字符串长度为2

    Javascript里,想把一个整数转换成字符串,字符串长度为2.  想把一个整数转换成字符串,字符串长度为2,怎么弄?比如 1 => "01"11 => " ...

  3. Linux c字符串中不可打印字符转换成16进制

    本文由 www.169it.com 搜集整理 如果一个C字符串中同时包含可打印和不可打印的字符,如果想将这个字符串写入文件,同时方便打开文件查看或者在控制台中打印出来不会出现乱码,那么可以将字符串中的 ...

  4. java字符串转义,把&lt;&gt;转换成<>等字符【原】

    java字符串转义,把<>转换成<>等字符 使用的是commons-lang3-3.4 中的StringEscapeUtils类 package test; import ja ...

  5. 坑爹的PostgreSQL的美元符号(有时需要替换成单引号)

    今天想在PostgeSQL数据库里建一个存储过程(或函数也行),由于对存储过程比较生疏,上网搜了很多教程和源代码例子,照着写,发现怎么都不行,甚至把网上教程包括官方教程的源代码原封不动的复制下来一执行 ...

  6. javascript消除字符串两边空格的两种方式,面向对象和函数式编程。python oop在调用时候的优点

    主要是javascript中消除字符串空格,比较两种方式的不同 //面向对象,消除字符串两边空格 String.prototype.trim = function() { return this.re ...

  7. JavaScript去除字符串两边空格trim

    去除字符串左右两端的空格,在大部分编程语言中,比如PHP.vbscript里面可以轻松地使用 trim.ltrim 或 rtrim实现.但在js中却没有这3个内置方法,需要手工编写.下面的实现方法是用 ...

  8. 为什么 JSON 接口的数据都要加双引号!!!不能用单引号

    原因是:Javascript 在很多时候会把 JSON 对象里面没有双引号包围的值,当做数值处理.比如: {"a":987654321} 这个 JSON 里头的变量 a,会被当做一 ...

  9. JSON 的标准:双引号而非单引号!

    刚刚测试发现一段很简单的.看似正确的代码却是错误的: <?php $json_str = "{'name':'Eric', 'age':23}"; var_dump(json ...

随机推荐

  1. 【codeforces 733F】 Drivers Dissatisfaction

    http://codeforces.com/problemset/problem/733/F (题目链接) 题意 给出一张n个点的无向图,每一条变有两个特征值:${w,c}$:分别表示这条边的权值为$ ...

  2. ListView的使用-模拟微博随便看看栏目【执行与优化】

    今天我们来讲述一下如何使用ListView来模仿微博随便看看栏目(ps:这是老师布置的作业,所以…),在前篇博客中,我们讲述了细解ListView之自定义适配器的使用,所以本篇我们不以特别详细的讲述( ...

  3. jsp使用servlet实现验证码

    在进行表单设计中,验证码的增加恰恰可以实现是否为“人为”操作,增加验证码可以防止网站数据库信息的冗杂等... 现在,小编将讲述通过servlet实现验证码: 验证码作为一个图片,在页面中为“画”出来的 ...

  4. hdu3982 直线切多边形 【WA中...】

    题意:有一块蛋糕,上面有一颗cherry.用刀子切n次,求切完之后有cherry的那部分的面积 My solution: 先做一个大矩形,使cake内切于这个大矩形.如图: 然后不断切这个大矩形,每次 ...

  5. Objective-C Runtime与黑客帝国

    Runtime的消息机制让我容易想起黑客帝国的Matrix.而OC语言,就像是架构在C语言真实世界上的Matrix世界,Runtime接管了这个虚拟世界到真实世界的承接. 在黑客帝国里,Matrix的 ...

  6. 05.K米评测

    体验产品:K米Android版本 软件版本:4.3.0 设备型号:魅蓝note2 操作系统: Android5.1 体验时间:2016.10.15-2016.10.15 体验人:黄瑞钰(0314025 ...

  7. range和xrange梳理

    一.python2.7 range 用户获取指定范围内的数,range([start,] stop[, step]) >>> range(1,5) #代表从1到5(不包含5) [1, ...

  8. JZOJ P1830[9.30]送牛奶

    传送门 临近NOIp,写一些简单题. 二分+BFS,注意的是要把数组开小点,有效减少memset的时间. //OJ 1830 //by Cydiater //2016.9.22 #include &l ...

  9. mac OS(OS X)的OI编译环境配置指南

    编译环境:gdb+Atom 如何安装gdb: http://logic0.blog.163.com/blog/static/1889281462014183271283/   Atom下载地址: ht ...

  10. Docker入门教程(一)介绍

    http://dockone.io/article/101 Docker入门教程(一)介绍 [编者的话]DockerOne组织翻译了Flux7的Docker入门教程,本文是系列入门教程的第一篇,介绍了 ...