如何在Node.js(Windows系统上)的控制台中输出表情符号?

19
在Windows上,控制台有一些基本的表情符号支持。例如,如果我输入 ,我可以得到一个单色字形。我可以从PowerShell或C#控制台应用程序或Python输出字符串,它们都可以很好地显示这些字符。
然而,在Node.js中,我只能显示几个表情符号(例如),但其他表情符号却不能正常显示(取而代之的是)。然而,如果我使用这些字符抛出一个字符串,它们将正确显示。
console.log('  ☕ ');
throw '  ☕ ';

如果我运行上述脚本,输出结果是:
 � ☕

C:\Code\emojitest\emojitest.js:2
throw '  ☕ '; 
^
  ☕
有没有办法在不报错的情况下正确输出这些表情符号?或者说这个异常是发生在标准Node.js API之外的地方?

2
嗯,你比其他人都走得更远。找到支持字形的字体通常是主要障碍。但是�是一个编码问题,这并不完全不寻常,因为滚动字形在高位平面中,而咖啡杯则不在其中。你可能应该关注为什么它在stderr中呈现时是可以的,比如在throw中,但在stdout中却不行。 - Hans Passant
@HansPassant 直接写入 stderr 与直接写入 stdout 没有任何区别,可能需要查看 Node.js 如何处理抛出... - bdukes
似乎是一个Windows的问题。在我的Mac上它运行得很好。 - hasen

@BrianNixon,是的,好问题,但我所做的只是切换节点版本(从7.10.0到6.10.3,通过nvm),并运行相同的脚本: v7.10.0 我很荣幸成为您的忠实仆人,~ npm ☕

v6.10.3 我很荣幸成为您的忠实仆人,~ npm ☕
- bdukes
@BrianNixon,我确实需要确保文件使用正确的编码进行保存,这可能可以解释你的测试结果。 - bdukes
显示剩余4条评论
4个回答

16
您所需要的可能需要改变libuv的方式才能实现。 当在Windows上向stdoutstderr写入并且流是TTY时,控制台或您自己写入的文本将被转换为UTF-16。在此过程中,libuv将拒绝输出代理对,而是发出替换字符U+FFFD,用于超出BMP的任何代码点。

这是问题所在 uv/src/win/tty.c

  /* We wouldn't mind emitting utf-16 surrogate pairs. Too bad, the */
  /* windows console doesn't really support UTF-16, so just emit the */
  /* replacement character. */
  if (utf8_codepoint > 0xffff) {
    utf8_codepoint = UNICODE_REPLACEMENT_CHARACTER;
  }

由于Node使用MultiByteToWideChar()将UTF-8转换为UTF-16(该方法会发出代理对),然后将消息写入控制台,因此throw消息可以正确显示。 (请参见src/node.cc中的PrintErrorString()。)

注意:已提交拉取请求以解决此问题。


听起来不错。问题中缺少的细节是OP可能使用的是Win10操作系统。在这个版本中,对控制台进行了大量调整,主要是为了扩展对新Linux子系统的支持。 - Hans Passant
那么,一个修改过的 libuv(可以接受超出 BMP 范围的代码点并使用 MultiByteToWideChar)在 Windows 10 上能够工作吗?-- 听起来像是一个有效的功能请求 :) - Hugues M.
@Hugues Moreau:我认为修改_libuv_以输出UTF-16代理对将允许在支持它们的Windows控制台中显示Node流中的非BMP字符。特别是不必使用MultiByteToWideChar();删除此处所示的代码行,然后添加一个else到以下块以发出代码点的代理对应该就足够了。但是我没有设置Node.js的构建环境(或者今天没有时间或倾向去创建一个!)来尝试它。 - Brian Nixon
1
@Hugues Moreau:是的,与_libuv_开发人员商讨这个问题似乎值得一试。我不太了解这个库,无法判断引入版本检查或配置标志有多容易,这样的更改可能会破坏多少兼容性,甚至实现它的原理是什么。 - Brian Nixon

2
免责声明:我没有解决方案,我探究了在Windows 10上使用工具打印表情符号与异常处理有何不同之处。希望这可能为解决问题提供一些线索,或许有人能够识别出一些东西并提出解决方案。
看起来Node在Windows上的异常报告代码调用了一个不同的Windows API,这个API支持Unicode更好。
让我们看看Node 7.10的源代码: ReportExceptionAppendExceptionLinePrintErrorString
在`PrintErrorString`函数中,Windows特定部分检测输出类型(tty/console或者其他): - 对于非tty/console的上下文环境,它将会打印到stderr(例如,如果你将输出重定向到文件) - 在一个cmd控制台(没有重定向情况下),它将使用MultiByteToWideChar()转换文本,并把结果传递WriteConsoleW()
如果我使用ConEmu运行你的程序(比让标准的cmd支持unicode和emoji更方便——是的,我有点懒),我看到了与你看到的类似的东西:`console.log`无法打印emoji,但是异常信息中的emoji能够正常打印(甚至包括滚动图案)。
如果我将所有输出重定向到一个文件中(node test.js > out.txt 2>&1,在Windows cmd中也适用),我会在两种情况下得到"干净"的Unicode。
因此,当一个程序在Windows控制台上打印到stdoutstderr时,控制台会在打印之前做一些(不好的)重新编码工作。当程序直接使用Windows控制台API(使用MultiByteToWideChar进行转换,然后使用WriteConsoleW()写入控制台)时,控制台显示完整未经过改变的表情符号。
当JS程序使用console API记录内容时,Node可能会尝试(在Windows上)检测控制台并执行与报告异常相同的操作。请参见@BrianNixon's answer,该答案解释了libuv中实际发生的事情。

1
下一个 "Windows Terminal"(来自 Kayla Cinnamon)和 Microsoft/Terminal 项目应该能够显示表情符号。 这将从2019年6月开始提供。通过使用 Consolas 字体,将提供部分 Unicode 支持。
请求正在 Microsoft/Terminal 问题387 中进行中。 Microsoft/Terminal问题190 正式要求"在Windows控制台中添加表情支持",但仍存在一些问题(截至2019年3月)。
我几天前将Win10从1803升级到了1809,现在所有字符>=U+10000(使用4个或更多字节的UTF-8)都无法显示。 我还尝试了最新的内部版本(Windows 10 Insider Preview 18358.1 (19h1_release)),不幸的是,这个bug仍然存在。

1
在控制台中记录表情符号有一种简单的方法:
console.log("\u{1F9E1} Do what you love or love what you do!");

console.log("\u{1F3AF}");

以十六进制格式引用的表情符号代码参考:https://www.w3schools.com/charsets/ref_emoji.asp


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接