我查了很多与此相关的问题,但找不到能解决我问题的东西。基本上,我想将一个 UTF-8 编码的字符串存储在一个变量中,然后将该字符串用作文件名。
例如,我正在尝试下载 YouTube 视频。如果我们打印视频标题,就会出现非英语字符(ytd
这里是 youtube-dl
):
./ytd https://www.youtube.com/watch?v=GWYndKw_zbw -e
输出:[LEEPLAY] 시티팝 입문 City Pop MIX(播放列表)
但是如果我将它存储在一个变量中并打印它,韩文字符将被忽略:
$vtitle= ./ytd https://www.youtube.com/watch?v=GWYndKw_zbw -e
$vtitle
输出:[LEEPLAY] City Pop MIX(播放列表)
最佳答案
有关 PowerShell 如何与外部程序交互的全面概述,包括向它们发送数据,请参阅 this answer .
当 PowerShell 解释来自外部程序的输出(例如您的情况下的 ytd
)时,它假定输出使用反射(reflect)在 中的字符编码[控制台]::OutputEncoding
.
注意:
解释 是指 PowerShell 捕获(例如,
$output = ...
)、中继(例如,... | Select-String ...
),或重定向(例如,...> output.txt
>) 外部程序的输出。相比之下,直接打印到显示器可能不会受到影响,因为此时不涉及 PowerShell,并且某些 CLI 在其标准输出未重定向为直接打印时会调整其行为到具有完整 Unicode 支持的控制台(这解释了为什么当
ytd
的输出直接打印到它时字符在您的控制台中看起来像预期的那样)。
如果 [Console]::OutputEncoding
报告的编码与手头的外部程序使用的编码不同,PowerShell 误解 输出。
要解决此问题,您必须(暂时)设置 [Console]::OutputEncoding]
以匹配外部程序使用的编码。
例如,我们假设一个输出 UTF-8 编码文本的可执行文件 foo.exe
:
# Save the current encoding and switch to UTF-8.
$prev = [Console]::OutputEncoding
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()
# PowerShell now interprets foo's output correctly as UTF-8-encoded.
# and $output will correctly contain CJK characters.
$output = foo https://example.org -e
# Restore the previous encoding.
[Console]::OutputEncoding = $prev
重要:
[Console]::OutputEncoding
默认情况下 反射(reflect)了与遗留系统区域设置的 OEM 代码页 关联的编码,如所报告的通过chcp
(例如美国英语系统上的437
)。- 最新版本的 Windows 10 现在允许将系统区域设置设置为代码页
65001
(UTF-8)(自 Window 10 版本 1909 起,该功能仍处于测试阶段),这很好,考虑到大多数现代命令行实用程序“使用”UTF-8 - 但请注意,进行此系统范围更改已经很远-达到后果 - 参见this answer .
- 最新版本的 Windows 10 现在允许将系统区域设置设置为代码页
根据手头的具体程序,youtube-dl
, js2010 has discovered如果您通过 --encoding utf-16
,则在变量中捕获无需额外的努力。
之所以可行,是因为生成的 UTF16-LE 编码输出前面有 BOM(字节顺序标记)。
(请注意,--encoding utf-8
不 起作用,因为 youtube-dl
不> 发出 BOM。)
Windows PowerShell 能够检测并正确解码 UTF-16LE 编码和 UTF-8 编码的文本而不管有效的[Console]::OutputEncoding]
当且仅当输出前面有 BOM。
注意事项:
这在 PowerShell Core(v6+,在任何支持的平台上)中不有效。
即使在 Windows PowerShell 中,您也很少能够利用这种模糊的行为,因为在stdout 输出 中使用 BOM 是非典型(它是通常仅用于文件)。
关于使用 UTF-8 编码的 Powershell 字符串变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58438095/