Какую кодировку использует nodejs для аргументов в child_process.spawn и child_process.execFile? - PullRequest
0 голосов
/ 07 июня 2019

В 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 ожидает аргументы в латинской кодировке. Если мое приложение nodejs вызывает child_process.execFile("id3v2",["--titl",myString], {encoding:"latin1"), то как nodejs закодирует myString в аргументах?

Я вижу, что execFile и spawn оба принимают аргумент "кодирования". Но документы nodejs говорят: «Опция кодирования может использоваться для указания кодировки символов, используемой для декодирования вывода stdout и stderr». В документах ничего не говорится о кодировке args.

1 Ответ

0 голосов
/ 07 июня 2019

Ответ: NodeJS всегда кодирует аргументы как 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 (или латиница 1 в последнем случае).В результате у меня есть четыре строки, содержимое которых, как я однозначно знаю, является правильным.

(я понимаю, что виртуальные машины Javascript обычно хранят свои строки как 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, были кодировками UTF-8из строк a, pound, pound2, skull.

Так как мы можем передать аргументы latin1 из nodejs?

Приведенное выше объяснение показывает, что это невозможно для nodejs впередать любой латинский символ в диапазоне 127..255 в child_process.spawn / execFile.Но есть аварийный люк с участием child_process.exec:

  • Пример: эта строка "A £ ☠"
  • хранится внутри в UTF16 Javascript как "\ u0041 \ u00A3 \ u2620"
  • , закодированный в UTF-8 как "\ x41 \ xC2 \ xA3 \ xE2 \ x98 \ xA0"
  • , закодированный в латинице 1 как "\ x41 \ xA3?"(череп и скрещенные кости невыразимы в латинице 1)
  • Символы Unicode 0-127 такие же, как символы latin1, и кодируются в utf-8 так же, как символы latin1
  • Unicode 128-255то же самое, что и latin1, но кодируется по-разному
  • Unicode-символы 256+ не существуют в latin1 /.
// 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) => { ... });
...