记一次nodejs使用require('child_process').spawn调用.bat/.cmd脚本遇到的中文乱码问题

根据客户需要开发了一个前后端程序,大概流程如下:

  1. 双击start.cmd启动nodejs服务器;
  2. 用户在web页面填写部分参数,提交到后台;
  3. 后端使用nodejs接收前端参数,然后根据参数复写服务器上的配置文件;
  4. 之后使用require('child_process').spawn调用服务器上的批处理脚本;
  5. 脚本中调用了ANSYS的可执行文件compute.bat来计算、转换3D模型数据;
  6. 可执行文件compute.bat执行过程中会输出一些运行提示文本到一个临时文件temp.dat中,提示文本中包含有中文;
  7. 使用nodejs监听临时文件的变动,从中提取出提示文本;
  8. 使用WebSocket像前端推送这些提示文本,然后前端展示在界面上。

start.cmd执行:
image.png

web页面参数:
image.png

临时文件temp.dat内容:
image.png

客户端收到WebSocket消息后展示如:
image.png

整个流程中,1、7、8环节都有中文字符

  • 启动nodejs服务器的命令提示符窗口中的标题,运行过程打印的提示文本;
  • compute.bat执行过程输出到temp.dat中的文本;
  • nodejs读取temp.dat中的文本。

首先,start.cmd、compute.bat、temp.dat文件本身有相应的编码,这些批处理文件保存的编码会影响输出文本的编码。

首先start.cmd是我用创建的,编码为UTF-8,双击文件打开后,
image.png
文件内容

@ECHO OFF
title 启动服务器
node ./index.js
pause

双击start.cmd时,是调用了cmd.exe命令提示符执行里面的脚本,而命令提示符本身也有自己的编码方式,可以输入chcp命令查看:
image.png

活动代码页: 936

936就表示命令提示符使用的GBK编码方式,而start.cmd文件使用的UTF-8,二者不一致导致了乱码,所以解决的方式就是使二者保持一致,两种修改方式:

  1. 将start.cmd重新以GBK编码方式保存;
  2. 修改命令提示符的编码方式为UTF-8,传送门——设置CMD默认代码页为65001或936

此时,双击start.cmd,标题就正常显示了
image.png

由于compute.bat是客户提供的,temp.dat是compute.bat生成的,这两个编码是一致的,都是ANSI(ANSI是Windows独有的,严格来说不能称之为编码类型,传送门——ANSI是什么编码?)。ANSI在国内一般就是GBK编码。

在使用nodejs读取temp.dat内容的时候,由于temp.dat是GBK编码,所以有如下代码:

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

var result = fs.readFileSync("temp.dat", "binary");
var text = iconv.decode(Buffer.from(result, "binary"), "GBK");
console.log("【原有内容】" + text);

nodejs本身不支持GBK,这里用到了iconv-lite模块来从读取到的Buffer以GBK方式解码,结果正常展示:
image.png

这里,有三个地方的编码需要保持一致:temp.dat、nodejs处理的编码方式、命令提示符的编码,否则展示在控制台的中文就会乱码。

由于temp.dat是compute.bat脚本生成的,而compute.bat脚本文件本身以何种编码方式保存会影响输出的编码,这一点也是不能忽略的。

另外,compute.bat执行过程发生的错误,如果nodejs捕获到了,也是需要使用GBK方式进行解码的:


  var {spawn } = require('child_process');

  const bat = spawn('cmd.exe', ['/c', "compute.bat"], {
    encoding: "buffer"
  });

  bat.stderr.on('data', (stderr) => {
    var errStr = iconv.decode(Buffer.from(stderr, "binary"), "GBK");
    console.error(errStr);
  });

  bat.on('exit', (code) => {
    if (code === 0) {
      // 正常退出
      cb();
    } else {
      cb({ code: code });
    }
  });

由于第一次使用nodejs操作文件构建稍微复杂点的程序,踩了很多坑,这篇文章主要针对过程中的编码问题简单介绍。也是第一回在思否发稍微长点的文章,文笔粗糙,各位不喜勿喷。

你可能感兴趣的