Ответ: 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) => { ... });