【问题标题】:What encoding does nodejs use for arguments in child_process.spawn and child_process.execFile?nodejs 对 child_process.spawn 和 child_process.execFile 中的参数使用什么编码?
【发布时间】:2019-06-07 15:12:31
【问题描述】:

在 NodeJS 中,child_process.execFile.spawn 采用这个参数:

  • args <string[]> 字符串参数列表。

NodeJS 如何对你传入这个数组的字符串进行编码?

上下文:我正在编写一个 nodejs 应用程序,它将元数据(通常包括非 ascii 字符)添加到 mp3。

  • 我知道ffmpeg 需要utf8 编码的参数。如果我的 nodejs 应用调用 child_process.execFile("ffmpeg",["-metadata","title="+myString], {encoding:"utf8"),那么 nodejs 将如何在参数中编码 myString
  • 我知道 id3v2 需要 latin1 编码的参数。如果我的 nodejs 应用调用 child_process.execFile("id3v2",["--titl",myString], {encoding:"latin1"),那么 nodejs 将如何在参数中编码 myString

我看到execFilespawn 都采用“编码”参数。但是 nodejs 文档说“编码选项可用于指定用于解码 stdout 和 stderr 输出的字符编码。”文档对args 的编码只字未提。

【问题讨论】:

    标签: node.js utf-8 character-encoding child-process


    【解决方案1】:

    答案:NodeJS 总是将 args 编码为 UTF-8。

    我编写了一个简单的 C++ 应用程序,它显示了传递到其 argv 的字节的原始真相:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
      printf("argc=%u\n", argc);
      for (int i = 0; i < argc; i++)
      {
        printf("%u:\"", i);
        for (char *c = argv[i]; *c != 0; c++)
        {
          if (*c >= 32 && *c < 127)
            printf("%c", *c);
          else
          {
            unsigned char d = *(unsigned char *)c;
            unsigned int e = d;
            printf("\\x%02X", e);
          }
        }
        printf("\"\n");
      }
      return 0;
    }
    

    在我的 NodeJS 应用程序中,我得到了一些我确信知道它们来自哪里的字符串:

    const a = Buffer.from([65]).toString("utf8");
    const pound = Buffer.from([0xc2, 0xa3]).toString("utf8");
    const skull = Buffer.from([0xe2, 0x98, 0xa0]).toString("utf8");
    
    const pound2 = Buffer.from([0xa3]).toString("latin1");
    

    toString 的参数表示缓冲区中的原始字节应该被理解为缓冲区是 UTF-8(或最后一种情况下的 latin1)。结果是我有四个字符串,我明确知道它们的内容是正确的。

    (我知道 Javascript VM 通常将其字符串存储为 UTF16?在我的实验中 pound 和 pound2 的行为相同的事实证明了字符串的出处并不重要。)

    最后我用这些字符串调用了 execFile:

    child_process.execFileAsync("argcheck",[a,pound,pound2,skull],{encoding:"utf8"});
    child_process.execFileAsync("argcheck",[a,pound,pound2,skull],{encoding:"latin1"});
    

    在这两种情况下,nodejs 传递给 argv 的原始字节都是字符串 a,pound,pound2,skull 的 UTF-8 编码。

    那么我们如何从 nodejs 传递 latin1 参数呢?

    上面的解释表明 nodejs 不可能将 127..255 范围内的任何 latin1 字符传递给 child_process.spawn/execFile。但是有一个涉及 child_process.exec 的逃生舱:

    • 示例:此字符串“A £ ☠”
    • 在 Javascript 的 UTF16 内部存储为“\u0041 \u00A3 \u2620”
    • 以 UTF-8 编码为“\x41 \xC2\xA3 \xE2\x98\xA0”
    • 在 latin1 中编码为“\x41 \xA3 ?” (骷髅和交叉骨在拉丁语中无法表达)
    • Unicode 字符 0-127 与 latin1 相同,编码为 utf-8 与 latin1 相同
    • Unicode 字符 128-255 与 latin1 相同,但编码不同
    • latin1/ 中不存在 256+ 的 Unicode 字符。
    // this would encode them as utf8, which is wrong:
    execFile("id3v2", ["--comment", "A £ ☠", "x.mp3"]);
    
    // instead we'll use shell printf to bypass nodejs's wrongful encoding:
    exec("id3v2 --comment \"`printf "A \xA3 ?"`\" x.mp3");
    

    这是一种将“A £ ☠”之类的字符串转换为“A \xA3 ?”之类的字符串的便捷方法,准备传递给child_process.exec:

    const comment2 = [...comment]
      .map(c =>
        c <= "\u007F" ? c : c <= "\u00FF"
        ? `\\x${("000" + c.charCodeAt(0).toString(16)).substr(-2)}` : "?")
        )
      .join("");
    
    const cmd = `id3v2 --comment \"\`printf \"${comment2}\"\`\" \"${fn}\"`;
    
    child_process.exec(cmd, (e, stdout, stderr) => { ... });
    

    【讨论】:

      猜你喜欢
      • 2016-03-16
      • 1970-01-01
      • 1970-01-01
      • 2022-01-25
      • 1970-01-01
      • 2015-07-02
      • 2020-06-05
      • 1970-01-01
      相关资源
      最近更新 更多