windows 亂碼之 gbk 與 cp936
在使用 node 子進程執行 cmd 命令時, 獲取到的輸出是亂碼的.
const { execSync } = require('child_process')
const res = execSync(`echo nihao 你好`)
console.log(`resres`, String(res))

事發原因
這是由於控制台輸入的編碼與 node 程序中使用的編碼不一致導致的.
相關疑問
可能你要問: 那是不是都改為 utf8 就行了? 並不行. 這就是 windows 控制台沒有默認是 utf8 的原因, 並且 utf8 與 gbk 也不是兼容的.
如果理解不兼容?
假設你修改 windows 控制台為 cp65001(utf8) 之后, 如果輸出的文本編碼是 utf8 的, 那么可以正常顯示. 但是如果在控制台中運行了某個程序, 他輸入的文本是 gbk 的, 那依然會亂碼.
查看控制台使用的編碼:
chcp

你可能要問: widnows 默認 gbk , 是不是代表它除了編碼問題之外, 都顯示幾乎所有的文字? 並不行. 這就是為什么還有 utf8 utf-16 的原因… gbk 只是在制定這個規范的時候, 能顯示幾乎所有中國的常見漢字. 那些一百來划的字或者生僻字, 就可能不能顯示了.
所以我認為, 編碼問題並沒有完美的解決方案. 那么在中國的 windows 環境下就暫認為都是 gbk 吧, 在基於這個的前提下, 我們就可以進行轉換了.
解決方法
在 node 中可以使用 iconv-lite 來進行轉換.
const { execSync } = require('child_process')
const { decode } = require('iconv-lite')
const res = execSync(`echo nihao 你好`)
console.log(`resres`, decode(res, `gbk`) )

擴展知識
在 windows 上我們可以用 chcp 獲取編碼來判斷是不是要進行 gbk 轉換, 如果不是的話, 會轉換錯誤.
所以最好進行一下判斷, 例如:
// 判斷當前環境是不是 gbk 編碼
const isGBK = require(`child_process`).execSync(`chcp`, {encoding: `utf8`}).match(/936/)
當然, 也可以粗暴一點直接判斷是不是中文環境, 是則使用 gbk:
/** * 判斷當前系統是不是中文 * @returns */
function isZh(){
return [
// 代碼頁為 936
() => require(`child_process`).execSync(`chcp`, {encoding: `utf8`}).match(/936/),
// 注冊表安裝語言為 0804
() => require(`child_process`).execSync(`reg query "HKEY_LOCAL_MACHINE\\SYSTEM\\ControlSet001\\Control\\Nls\\Language" /v InstallLanguage`, {encoding: `utf8`}).match(/0804/),
// 查看版本時輸出雙字節字符
() => require(`child_process`).execSync(`ver`, {encoding: `utf8`}).match(/[\u4e00-\u9fa5]/),
// 控制台語言配置為 zh_CN
() => process.env.LANG.match(`zh_CN`),
].some(item => {
try {
return item()
} catch (error) {
return false
}
})
}
注, 一般情況下在其他系統下例如 mac 上是沒有這個問題的, 應該不會亂碼, 不需要轉 GBK.
關於 cp936 與 gbk
cp936 是 gbk, 936 是因為 IBM 發明代碼頁時, gbk 在代碼頁的第 936 頁.
關鍵字:NLS,cp936,GBK
- NLS(Native Language System)
- cp - code page
- GB - guo biao 國標
- GBK - guo biao kuo 國標擴展 - cp936
- ANSI - 表示默認
在“控制面板”-“區域和語言選項”-“高級”-“代碼頁轉換表”可以看出“936 (ANSI/OEM - 簡體中文 GBK)”
注, 某些文章說它們其實不一樣~
參考
- http://t.zoukankan.com/python168-p-12729259.html
- https://blog.csdn.net/iteye_7333/article/details/82063777
- https://www.cnblogs.com/finallyliuyu/archive/2013/05/10/3071023.html
- https://www.zhihu.com/question/65219842
- https://linux.cn/article-1213-1.html
- https://www.cnpython.com/qa/286302
-
nodejs(三) —- nodejs進程與子進程 https://blog.csdn.net/weixin_33885253/article/details/86249211
- Nodejs進階:如何玩轉子進程(child_process) https://www.cnblogs.com/chyingp/p/node-learning-guide-child_process.html
- node如何手動關閉子進程 https://segmentfault.com/q/1010000012575278
