了解 VS Code 和 PowerShell 中的文件编码
使用 VS Code 创建和编辑 PowerShell 脚本时,请务必使用正确的字符编码格式保存文件。
什么是文件编码,为什么很重要?
VS Code 管理人类在缓冲区中输入字符字符串以及读取/写入文件系统的字节块之间的接口。 当 VS Code 保存文件时,它使用文本编码来确定每个字符的字节数。 有关详细信息,请参阅 about_Character_Encoding。
同样,当 PowerShell 运行脚本时,它必须将文件中的字节转换为字符,以将文件重新构造为 PowerShell 程序。 由于 VS Code 写入文件,并且 PowerShell 读取该文件,因此需要使用相同的编码系统。 分析 PowerShell 脚本的过程包括:
VS Code 和 PowerShell 都安装了合理的默认编码配置。 但是,PowerShell 使用的默认编码随 PowerShell 6 的发布而更改。 若要确保在 VS Code 中使用 PowerShell 或 PowerShell 扩展时没有问题,需要正确配置 VS Code 和 PowerShell 设置。
编码问题的常见原因
当 VS Code 或脚本文件的编码与 PowerShell 的预期编码不匹配时,会发生编码问题。 PowerShell 无法自动确定文件编码。
使用 7 位 ASCII 字符集中的字符时,更有可能遇到编码问题。 例如:
- 扩展的非字母字符,如 em-dash(
—
)、非中断空格("
) - 着色拉丁字符(
É
,ü
) - 西里尔文等非拉丁文字符(
Д
,Ц
) - 中日韩字符(
本
、화
、が
)
编码问题的常见原因是:
- VS Code 和 PowerShell 的编码尚未从默认值更改。 对于 PowerShell 5.1 及更低版本,默认编码不同于 VS Code。
- 另一个编辑器已打开并覆盖新编码中的文件。 这通常发生在 ISE 中。
- 该文件在与 VS Code 或 PowerShell 预期不同的编码中签入源代码管理。 当协作者使用不同的编码配置使用编辑器时,可能会发生这种情况。
如何判断何时遇到编码问题
通常,编码错误在脚本中呈现为分析错误。 如果在脚本中发现奇怪的字符序列,可能是问题。 在下面的示例中,en-dash(–
)显示为字符 â€"
:
Send-MailMessage : A positional parameter cannot be found that accepts argument 'Testing FuseMail SMTP...'.
At C:\Users\<User>\<OneDrive>\Development\PowerShell\Scripts\Send-EmailUsingSmtpRelay.ps1:6 char:1
+ Send-MailMessage â€"From $from â€"To $recipient1 â€"Subject $subject ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Send-MailMessage], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.SendMailMessage
出现此问题的原因是 VS Code 将 UTF-8 中的字符 –
编码为字节 0xE2 0x80 0x93
。 将这些字节解码为 Windows-1252 时,它们将被解释为字符 â€"
。
你可能会看到的一些奇怪的字符序列包括:
-
â€"
而不是–
(短划线) -
â€"
而不是—
(em-dash) -
Ä2
而不是Ä
-
Â
而不是 -
é
而不是é
此方便的 参考 列出了指示 UTF-8/Windows-1252 编码问题的常见模式。
VS Code 中的 PowerShell 扩展如何与编码交互
PowerShell 扩展通过多种方式与脚本交互:
- 在 VS Code 中编辑脚本时,VS Code 会将内容发送到扩展。 语言服务器协议 强制在 UTF-8 中传输此内容。 因此,扩展无法获取错误的编码。
- 直接在集成控制台中执行脚本时,PowerShell 会直接从文件中读取这些脚本。 如果 PowerShell 的编码不同于 VS Code,则此处可能会出错。
- 当 VS Code 中打开的脚本引用 VS Code 中未打开的另一个脚本时,该扩展会回退到从文件系统加载该脚本的内容。 PowerShell 扩展默认为 UTF-8 编码,但使用 字节顺序标记或 BOM 检测来选择正确的编码。
假设无 BOM 格式的编码(如不带 BOM 的 UTF-8,Windows-1252),则会出现此问题。 PowerShell 扩展默认为 UTF-8。 该扩展无法更改 VS Code 的编码设置。 有关详细信息,请参阅 问题 #824。
选择正确的编码
不同的系统和应用程序可以使用不同的编码:
- 在 .NET Standard、Web 和 Linux 世界中,UTF-8 现在是主导编码。
- 许多 .NET Framework 应用程序使用 UTF-16。 出于历史原因,这有时称为“Unicode”,该术语现在指的是包括 UTF-8 和 UTF-16 的广泛 标准。
- 在 Windows 上,许多早于 Unicode 的本机应用程序默认继续使用 Windows-1252。
Unicode 编码也有字节顺序标记(BOM)的概念。 BOM 出现在文本的开头,告知解码器使用哪个编码文本。 对于多字节编码,BOM 还指示编码 字节。 BOM 设计为非 Unicode 文本中很少发生的字节,允许合理猜测 BOM 时文本为 Unicode。
BOM 是可选的,其采用在 Linux 世界中并不那么受欢迎,因为随处都使用了可靠的 UTF-8 约定。 大多数 Linux 应用程序假定文本输入在 UTF-8 中编码。 虽然许多 Linux 应用程序会识别并正确处理 BOM,但数字不会,导致使用这些应用程序操作的文本中的项目。
因此,:
- 如果主要使用 Windows 应用程序和 Windows PowerShell,则应首选使用 BOM 或 UTF-16 的 UTF-8 等编码。
- 如果跨平台工作,则应更喜欢使用 BOM 的 UTF-8。
- 如果主要在 Linux 关联的上下文中工作,则应首选 UTF-8 而不使用 BOM。
- Windows-1252 和 latin-1 本质上是应避免的旧编码(如果可能)。 但是,某些较旧的 Windows 应用程序可能依赖于它们。
- 值得注意的是,脚本签名 依赖编码的,这意味着对已签名脚本的编码更改将需要辞职。
配置 VS Code
VS Code 的默认编码为不带 BOM 的 UTF-8。
若要设置 VS Code 的编码,请转到 VS Code 设置(Ctrl+、),并设置 "files.encoding"
设置:
"files.encoding": "utf8bom"
一些可能的值为:
-
utf8
: [UTF-8] 没有 BOM -
utf8bom
:带 BOM 的 [UTF-8] -
utf16le
: 小端 [UTF-16] -
utf16be
:Big endian [UTF-16] -
windows1252
:[Windows-1252]
应在 GUI 视图中获取此内容的下拉列表,或在 JSON 视图中获取该下拉列表。
还可以在可能的情况下添加以下内容以自动检测编码:
"files.autoGuessEncoding": true
如果不希望这些设置影响所有文件类型,VS Code 还允许每种语言配置。 通过将设置放在 [<language-name>]
字段中来创建特定于语言的设置。 例如:
"[powershell]": {
"files.encoding": "utf8bom",
"files.autoGuessEncoding": true
}
还可以考虑安装适用于 Visual Studio Code 的 Gremlins 跟踪器。 此扩展显示某些容易损坏的 Unicode 字符,因为它们不可见或看起来像其他普通字符。
配置 PowerShell
PowerShell 的默认编码因版本而异:
- 在 PowerShell 6+ 中,默认编码为所有平台上没有 BOM 的 UTF-8。
- 在 Windows PowerShell 中,默认编码通常是 Windows-1252,这是 latin-1(也称为 ISO 8859-1)的扩展。
在 PowerShell 5+ 中,可以使用以下代码找到默认编码:
[psobject].Assembly.GetTypes() | Where-Object { $_.Name -eq 'ClrFacade'} |
ForEach-Object {
$_.GetMethod('GetDefaultEncoding', [System.Reflection.BindingFlags]'nonpublic,static').Invoke($null, @())
}
以下 脚本 可用于确定没有 BOM 的脚本推断的 PowerShell 会话编码。
$badBytes = [byte[]]@(0xC3, 0x80)
$utf8Str = [System.Text.Encoding]::UTF8.GetString($badBytes)
$bytes = [System.Text.Encoding]::ASCII.GetBytes('Write-Output "') + [byte[]]@(0xC3, 0x80) + [byte[]]@(0x22)
$path = Join-Path ([System.IO.Path]::GetTempPath()) 'encodingtest.ps1'
try
{
[System.IO.File]::WriteAllBytes($path, $bytes)
switch (& $path)
{
$utf8Str
{
return 'UTF-8'
break
}
default
{
return 'Windows-1252'
break
}
}
}
finally
{
Remove-Item $path
}
可以将 PowerShell 配置为更普遍地使用配置文件设置使用给定编码。 请参阅以下文章:
有关 Stack Overflow上的 PowerShell 编码的 答案。 关于在 PowerShell中处理无 BOM UTF-8 输入的 博客文章。
无法强制 PowerShell 使用特定的输入编码。 PowerShell 5.1 及更低版本,在 Windows 上运行,区域设置设置为 en-US,如果没有 BOM,则默认为 Windows-1252 编码。 其他区域设置可能使用不同的编码。 为了确保互操作性,最好使用 BOM 以 Unicode 格式保存脚本。
重要
接触 PowerShell 脚本的任何其他工具都可能会受到编码选择的影响,或者将脚本重新编码为另一种编码。
现有脚本
文件系统上已有的脚本可能需要重新编码为新的所选编码。 在 VS Code 的底部栏中,你将看到标签 UTF-8。 单击它可打开操作栏,然后选择“保存 编码。 现在可以为该文件选择新的编码。 有关完整说明,请参阅 VS Code 的编码。
如果需要重新编码多个文件,可以使用以下脚本:
Get-ChildItem *.ps1 -Recurse | ForEach-Object {
$content = Get-Content -Path $_
Set-Content -Path $_.Fullname -Value $content -Encoding UTF8 -PassThru -Force
}
PowerShell 集成脚本环境 (ISE)
如果还使用 PowerShell ISE 编辑脚本,则需要在那里同步编码设置。
ISE 应遵循 BOM,但也可以使用反射来 设置编码。 请注意,这不会在启动之间保留。
源代码管理软件
某些源代码管理工具(如 git)忽略编码;git 只跟踪字节。 其他项目(如 Azure DevOps 或 Mercurial)可能不会。 即使是一些基于 git 的工具也依赖于解码文本。
如果是这种情况,请确保:
- 在源代码管理中配置文本编码以匹配 VS Code 配置。
- 确保在相关编码中将所有文件签入源代码管理。
- 对通过源代码管理收到的编码的更改保持谨慎。 此键符号是一个差异,指示更改,但似乎没有任何变化(因为字节有但字符没有)。
协作者的环境
在配置源代码管理的基础上,请确保共享的任何文件上的协作者没有通过重新编码 PowerShell 文件替代编码的设置。
其他程序
读取或写入 PowerShell 脚本的任何其他程序均可重新编码。
一些示例包括:
- 使用剪贴板复制和粘贴脚本。 这种情况在以下情况下很常见:
- 将脚本复制到 VM
- 从电子邮件或网页复制脚本
- 将脚本复制到 Word 或 PowerPoint 文档Microsoft或传出
- 其他文本编辑器,例如:
- 记事本
- vim
- 任何其他 PowerShell 脚本编辑器
- 文本编辑实用工具,例如:
Get-Content
/Set-Content
/Out-File
- PowerShell 重定向运算符,例如
>
和>>
sed
/awk
- 文件传输程序,例如:
- 下载脚本时 Web 浏览器
- 文件共享
其中一些工具以字节而不是文本形式处理,但其他工具提供编码配置。 在需要配置编码的情况下,需要使其与编辑器编码相同,以防止出现问题。
在 PowerShell 中编码的其他资源
在 PowerShell 中,还有一些关于编码和配置编码的其他好文章值得阅读:
- about_Character_Encoding
Stack Overflow 上 PowerShell 编码的 摘要 - VS Code-PowerShell 上打开的编码问题以前的问题:
- 经典 Joel on Software 写出有关 Unicode
- .NET Standard 中的
编码