鍍金池/ 教程/ HTML/ 文本編碼
文本編碼
小結(jié)
API 走馬觀花
API 走馬觀花
迭代
小結(jié)
運(yùn)行
回調(diào)
需求
代碼設(shè)計(jì)模式
進(jìn)程介紹
模塊
工程目錄
小結(jié)
小結(jié)
遍歷目錄
小結(jié)
小結(jié)
API 走馬觀花
用途
NPM
小結(jié)
安裝
網(wǎng)絡(luò)操作介紹
二進(jìn)制模塊
什么是 NodeJS
命令行程序
靈機(jī)一點(diǎn)
域(Domain)
應(yīng)用場(chǎng)景
模塊路徑解析規(guī)則
文件拷貝

文本編碼

使用 NodeJS 編寫(xiě)前端工具時(shí),操作得最多的是文本文件,因此也就涉及到了文件編碼的處理問(wèn)題。我們常用的文本編碼有 UTF8 和 GBK 兩種,并且 UTF8 文件還可能帶有 BOM。在讀取不同編碼的文本文件時(shí),需要將文件內(nèi)容轉(zhuǎn)換為 JS 使用的 UTF8 編碼字符串后才能正常處理。

BOM 的移除

BOM 用于標(biāo)記一個(gè)文本文件使用 Unicode 編碼,其本身是一個(gè) Unicode 字符("\uFEFF"),位于文本文件頭部。在不同的 Unicode 編碼下,BOM 字符對(duì)應(yīng)的二進(jìn)制字節(jié)如下:

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

因此,我們可以根據(jù)文本文件頭幾個(gè)字節(jié)等于啥來(lái)判斷文件是否包含 BOM,以及使用哪種 Unicode 編碼。但是,BOM 字符雖然起到了標(biāo)記文件編碼的作用,其本身卻不屬于文件內(nèi)容的一部分,如果讀取文本文件時(shí)不去掉 BOM,在某些使用場(chǎng)景下就會(huì)有問(wèn)題。例如我們把幾個(gè) JS 文件合并成一個(gè)文件后,如果文件中間含有 BOM 字符,就會(huì)導(dǎo)致瀏覽器 JS 語(yǔ)法錯(cuò)誤。因此,使用 NodeJS 讀取文本文件時(shí),一般需要去掉 BOM。例如,以下代碼實(shí)現(xiàn)了識(shí)別和去除 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 轉(zhuǎn) UTF8

NodeJS 支持在讀取文本文件時(shí),或者在 Buffer 轉(zhuǎn)換為字符串時(shí)指定文本編碼,但遺憾的是,GBK 編碼不在NodeJS自身支持范圍內(nèi)。因此,一般我們借助 iconv-lite 這個(gè)三方包來(lái)轉(zhuǎn)換編碼。使用 NPM 下載該包后,我們可以按下邊方式編寫(xiě)一個(gè)讀取 GBK 文本文件的函數(shù)。

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

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

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

單字節(jié)編碼

有時(shí)候,我們無(wú)法預(yù)知需要讀取的文件采用哪種編碼,因此也就無(wú)法指定正確的編碼。比如我們要處理的某些 CSS 文件中,有的用 GBK 編碼,有的用 UTF8 編碼。雖然可以一定程度可以根據(jù)文件的字節(jié)內(nèi)容猜測(cè)出文本編碼,但這里要介紹的是有些局限,但是要簡(jiǎn)單得多的一種技術(shù)。

首先我們知道,如果一個(gè)文本文件只包含英文字符,比如 Hello World,那無(wú)論用 GBK 編碼或是 UTF8 編碼讀取這個(gè)文件都是沒(méi)問(wèn)題的。這是因?yàn)樵谶@些編碼下,ASCII0~128 范圍內(nèi)字符都使用相同的單字節(jié)編碼。

反過(guò)來(lái)講,即使一個(gè)文本文件中有中文等字符,如果我們需要處理的字符僅在 ASCII0~128 范圍內(nèi),比如除了注釋和字符串以外的JS代碼,我們就可以統(tǒng)一使用單字節(jié)編碼來(lái)讀取文件,不用關(guān)心文件的實(shí)際編碼是 GBK 還是 UTF8。以下示例說(shuō)明了這種方法。

1. GBK編碼源文件內(nèi)容:

    var foo = '中文';

2. 對(duì)應(yīng)字節(jié):

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

3. 使用單字節(jié)編碼讀取后得到的內(nèi)容:

    var foo = '{亂碼}{亂碼}{亂碼}{亂碼}';

4. 替換內(nèi)容:

    var bar = '{亂碼}{亂碼}{亂碼}{亂碼}';

5. 使用單字節(jié)編碼保存后對(duì)應(yīng)字節(jié):

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

6. 使用 GBK 編碼讀取后得到內(nèi)容:

    var bar = '中文';

這里的訣竅在于,不管大于 0xEF 的單個(gè)字節(jié)在單字節(jié)編碼下被解析成什么亂碼字符,使用同樣的單字節(jié)編碼保存這些亂碼字符時(shí),背后對(duì)應(yīng)的字節(jié)保持不變。

NodeJS 中自帶了一種 binary 編碼可以用來(lái)實(shí)現(xiàn)這個(gè)方法,因此在下例中,我們使用這種編碼來(lái)演示上例對(duì)應(yīng)的代碼該怎么寫(xiě)。

function replace(pathname) {
    var str = fs.readFileSync(pathname, 'binary');
    str = str.replace('foo', 'bar');
    fs.writeFileSync(pathname, str, 'binary');
}
上一篇:小結(jié)下一篇:安裝