使用Node.js处理前端代码文件的编码问题


Posted in Javascript onFebruary 16, 2016

使用 NodeJS 编写前端工具时,操作得最多的是文本文件,因此也就涉及到了文件编码的处理问题。我们常用的文本编码有 UTF8 和 GBK 两种,并且 UTF8 文件还可能带有 BOM。在读取不同编码的文本文件时,需要将文件内容转换为 JS 使用的 UTF8 编码字符串后才能正常处理。

BOM 的移除
BOM 用于标记一个文本文件使用 Unicode 编码,其本身是一个 Unicode 字符("\uFEFF"),位于文本文件头部。在不同的 Unicode 编码下,BOM 字符对应的二进制字节如下:

Bytes   Encoding
----------------------------
  FE FF    UTF16BE
  FF FE    UTF16LE
  EF BB BF  UTF8

因此,我们可以根据文本文件头几个字节等于啥来判断文件是否包含 BOM,以及使用哪种 Unicode 编码。但是,BOM 字符虽然起到了标记文件编码的作用,其本身却不属于文件内容的一部分,如果读取文本文件时不去掉 BOM,在某些使用场景下就会有问题。例如我们把几个 JS 文件合并成一个文件后,如果文件中间含有 BOM 字符,就会导致浏览器 JS 语法错误。因此,使用 NodeJS 读取文本文件时,一般需要去掉 BOM。例如,以下代码实现了识别和去除 UTF8 BOM 的功能。

function readText(pathname) {
  var bin = fs.readFileSync(pathname);

  if (bin[0] === 0xEF && bin[1] === 0xBB && bin[2] === 0xBF) {
    bin = bin.slice(3);
  }

  return bin.toString('utf-8');
}

GBK 转 UTF8
NodeJS 支持在读取文本文件时,或者在 Buffer 转换为字符串时指定文本编码,但遗憾的是,GBK 编码不在NodeJS自身支持范围内。因此,一般我们借助 iconv-lite 这个三方包来转换编码。使用 NPM 下载该包后,我们可以按下边方式编写一个读取 GBK 文本文件的函数。

var iconv = require('iconv-lite');

function readGBKText(pathname) {
  var bin = fs.readFileSync(pathname);

  return iconv.decode(bin, 'gbk');
}

单字节编码
有时候,我们无法预知需要读取的文件采用哪种编码,因此也就无法指定正确的编码。比如我们要处理的某些 CSS 文件中,有的用 GBK 编码,有的用 UTF8 编码。虽然可以一定程度可以根据文件的字节内容猜测出文本编码,但这里要介绍的是有些局限,但是要简单得多的一种技术。

首先我们知道,如果一个文本文件只包含英文字符,比如 Hello World,那无论用 GBK 编码或是 UTF8 编码读取这个文件都是没问题的。这是因为在这些编码下,ASCII0~128 范围内字符都使用相同的单字节编码。

反过来讲,即使一个文本文件中有中文等字符,如果我们需要处理的字符仅在 ASCII0~128 范围内,比如除了注释和字符串以外的JS代码,我们就可以统一使用单字节编码来读取文件,不用关心文件的实际编码是 GBK 还是 UTF8。以下示例说明了这种方法。

1. GBK编码源文件内容:

var foo = '中文';

2. 对应字节:

76 61 72 20 66 6F 6F 20 3D 20 27 D6 D0 CE C4 27 3B

3. 使用单字节编码读取后得到的内容:

var foo = '{乱码}{乱码}{乱码}{乱码}';

4. 替换内容:

var bar = '{乱码}{乱码}{乱码}{乱码}';

5. 使用单字节编码保存后对应字节:

76 61 72 20 62 61 72 20 3D 20 27 D6 D0 CE C4 27 3B

6. 使用 GBK 编码读取后得到内容:

var bar = '中文';

这里的诀窍在于,不管大于 0xEF 的单个字节在单字节编码下被解析成什么乱码字符,使用同样的单字节编码保存这些乱码字符时,背后对应的字节保持不变。

NodeJS 中自带了一种 binary 编码可以用来实现这个方法,因此在下例中,我们使用这种编码来演示上例对应的代码该怎么写。

function replace(pathname) {
  var str = fs.readFileSync(pathname, 'binary');
  str = str.replace('foo', 'bar');
  fs.writeFileSync(pathname, str, 'binary');
}
Javascript 相关文章推荐
关于hashchangebroker和statehashable的补充文档
Aug 08 Javascript
通过一段代码简单说js中的this的使用
Jul 23 Javascript
html文件中jquery与velocity变量中的$冲突的解决方法
Nov 01 Javascript
js模仿hover的具体实现代码
Dec 30 Javascript
JavaScript基础语法、dom操作树及document对象
Dec 02 Javascript
JavaScript实现横向滑出的多级菜单效果
Oct 09 Javascript
javascript求日期差的方法
Mar 02 Javascript
BootstrapTable+KnockoutJS自定义T4模板快速生成增删改查页面
Aug 01 Javascript
Angular ng-class详解及实例代码
Sep 19 Javascript
Angular.js项目中使用gulp实现自动化构建以及压缩打包详解
Jul 19 Javascript
vue watch关于对象内的属性监听
Apr 22 Javascript
vue 验证两次输入的密码是否一致的方法示例
Sep 29 Javascript
让图片跳跃起来  javascript图片轮播特效
Feb 16 #Javascript
Node.js本地文件操作之文件拷贝与目录遍历的方法
Feb 16 #Javascript
详解Node.js包的工程目录与NPM包管理器的使用
Feb 16 #Javascript
javascript每日必学之运算符
Feb 16 #Javascript
解析Node.js基于模块和包的代码部署方式
Feb 16 #Javascript
javascript每日必学之基础入门
Feb 16 #Javascript
快速掌握Node.js环境的安装与运行方法
Feb 16 #Javascript
You might like
php连接函数implode与分割explode的深入解析
2013/06/26 PHP
thinkphp实现like模糊查询实例
2014/10/29 PHP
Yii2 RESTful中api的使用及开发实例详解
2016/07/06 PHP
JavaScript自定义事件介绍
2013/08/29 Javascript
JavaScript中instanceof与typeof运算符的用法及区别详细解析
2013/11/19 Javascript
jQuery学习笔记之toArray()
2014/06/09 Javascript
javascript事件模型介绍
2016/05/31 Javascript
微信小程序 底部导航栏目开发资料
2016/12/05 Javascript
JS库中的Particles.js在vue上的运用案例分析
2017/09/13 Javascript
angular内置provider之$compileProvider详解
2017/09/27 Javascript
基于VUE.JS的移动端框架Mint UI的使用
2017/10/11 Javascript
谈谈JS中的!!
2017/12/07 Javascript
如何解决.vue文件url引用文件的问题
2019/01/18 Javascript
vant 解决tab切换插件标题样式自定义的问题
2020/11/13 Javascript
Python文件读取的3种方法及路径转义
2015/06/21 Python
python如何去除字符串中不想要的字符
2020/07/05 Python
PyQt5每天必学之组合框
2018/04/20 Python
python pandas修改列属性的方法详解
2018/06/09 Python
django2+uwsgi+nginx上线部署到服务器Ubuntu16.04
2018/06/26 Python
python实现在遍历列表时,直接对dict元素增加字段的方法
2019/01/15 Python
在Pandas中DataFrame数据合并,连接(concat,merge,join)的实例
2019/01/29 Python
Python爬虫实现验证码登录代码实例
2019/05/10 Python
python实现串口自动触发工作的示例
2019/07/02 Python
python绘制无向图度分布曲线示例
2019/11/22 Python
使用pytorch搭建AlexNet操作(微调预训练模型及手动搭建)
2020/01/18 Python
python 中的命名空间,你真的了解吗?
2020/08/19 Python
python 中 .py文件 转 .pyd文件的操作
2021/03/04 Python
CSS3教程(8):CSS3透明度指南
2009/04/02 HTML / CSS
德国高端单身人士交友网站:ElitePartner
2018/12/02 全球购物
迪士尼英国官方商店:shopDisney UK
2019/09/21 全球购物
您熟悉ORM(Object-Relation Mapping)吗?请谈谈您所理解的ORM
2016/02/08 面试题
毕业生的自我评价范文
2013/12/31 职场文书
2015年党员公开承诺事项
2015/04/27 职场文书
幼儿园教师读书笔记
2015/06/29 职场文书
mysql中between的边界,范围说明
2021/06/08 MySQL
SpringCloud Alibaba 基本开发框架搭建过程
2021/06/13 Java/Android