使用 PowerShell 在没有 BOM 的情况下以 UTF-8 格式写入文件

Using PowerShell to write a file in UTF-8 without the BOM

提问人:sourcenouveau 提问时间:4/8/2011 最后编辑:sourcenouveau 更新时间:9/29/2023 访问量:412433

问:

Out-File使用 UTF-8 时似乎强制执行 BOM:

$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath

如何使用 PowerShell 以没有 BOM 的 UTF-8 格式编写文件?

2021 年更新

自从我 10 年前写这个问题以来,PowerShell 发生了一些变化。查看下面的多个答案,他们有很多好信息!

编码 PowerShell UTF-8 字节顺序标记

评论

35赞 Signal15 11/27/2014
BOM = 字节顺序标记。放置在文件开头的三个字符 (0xEF,0xBB,0xBF),类似于“”
67赞 MichaelGG 4/2/2015
这非常令人沮丧。甚至第三方模块也会受到污染,例如尝试通过 SSH 上传文件?噗!“是的,让我们破坏每一个文件;这听起来是个好主意Microsoft。
10赞 Paul Shiryaev 7/9/2019
默认编码为 UTF8NoBOM,从 Powershell 版本 6.0 开始 learn.microsoft.com/en-us/powershell/module/...
3赞 Dragas 1/13/2020
谈论打破向后兼容性...
1赞 Bacon Bits 6/28/2022
我觉得应该注意的是,虽然 UTF-8 文件中的 BOM 确实会使许多系统窒息,但在 Unicode UTF-8 规范中,包含一个 BOM 是明确有效的

答:

296赞 sourcenouveau 4/8/2011 #1

用。NET 的 UTF8Encoding 类并传递给构造函数似乎有效:$False

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)

评论

66赞 Scott Muc 5/24/2011
呃,我希望这不是唯一的方法。
139赞 Roman Kuzmin 11/9/2011
一行就够了。此重载只写入不带 BOM 的 UTF8。[System.IO.File]::WriteAllLines($MyPath, $MyFile)WriteAllLines
6赞 Groostav 2/19/2015
在此处创建了 MSDN 功能请求:connect.microsoft.com/PowerShell/feedbackdetail/view/1137121/...
8赞 sschuberth 1/4/2017
请注意,这似乎需要是绝对的。WriteAllLines$MyPath
18赞 Shayan Toqraee 10/1/2017
@xdhmoore从中获取当前目录。如果打开 PowerShell,然后更改当前目录(使用 或 ),则不会更改,文件最终会位于错误的目录中。您可以通过 解决此问题。WriteAllLines[System.Environment]::CurrentDirectorycdSet-Location[System.Environment]::CurrentDirectory[System.Environment]::CurrentDirectory = (Get-Location).Path
6赞 jamhan 5/1/2013 #2

此脚本将DIRECTORY1中的所有.txt文件转换为不带 BOM 的 UTF-8,并将它们输出到DIRECTORY2

foreach ($i in ls -name DIRECTORY1\*.txt)
{
    $file_content = Get-Content "DIRECTORY1\$i";
    [System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}

评论

0赞 darksoulsong 9/8/2013
这个失败了,没有任何警告。我应该使用哪个版本的 powershell 来运行它?
5赞 BermudaLamb 3/25/2015
WriteAllLines 解决方案非常适合小文件。但是,我需要一个解决方案来处理较大的文件。每次我尝试将其与更大的文件一起使用时,我都会收到 OutOfMemory 错误。
-4赞 Krzysztof 5/6/2015 #3

这个对我有用(使用“默认”而不是“UTF8”):

$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath

结果是没有 BOM 的 ASCII。

评论

2赞 sourcenouveau 5/6/2015
根据 Out-File 文档,指定编码将使用系统当前的 ANSI 代码页,该代码页不是 UTF-8,正如我所要求的那样。Default
0赞 eythort 8/5/2016
这似乎对我有用,至少对 Export-CSV 有用。如果您在适当的编辑器中打开生成的文件,则文件编码是没有 BOM 的 UTF-8,而不是我所期望的 ASCII 的西拉丁文 ISO 9
0赞 emptyother 7/22/2017
如果无法检测到编码,许多编辑器会以 UTF-8 格式打开文件。
-3赞 Robin Wang 9/23/2015 #4

可以使用下面来获取没有 BOM 的 UTF8

$MyFile | Out-File -Encoding ASCII

评论

4赞 ForNeVeR 10/5/2015
否,它会将输出转换为当前的 ANSI 代码页(例如 cp1251 或 cp1252)。它根本不是 UTF-8!
1赞 Greg 12/11/2015
谢谢罗宾。这可能不适用于在没有 BOM 的情况下写入 UTF-8 文件,但 -Encoding ASCII 选项删除了 BOM。这样我就可以为 gvim 生成一个 bat 文件。.bat 文件在 BOM 上跳闸。
3赞 mklement0 1/21/2016
@ForNeVeR:你说得对,编码不是UTF-8,但它不是当前的ANSI代码页 - 你正在考虑; 真正的是 7 位 ASCII 编码,代码点 >= 128 被转换为文本实例。ASCIIDefaultASCII?
1赞 mklement0 1/21/2016
@ForNeVeR:您可能正在考虑“ANSI”或“扩展 ASCII”。尝试这样做以验证是否确实只有 7 位 ASCII: - 已音译为 .相比之下,(“ANSI”)将正确地保留它。-Encoding ASCII'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)ä?-Encoding Default
3赞 TNT 8/26/2016
@rob 对于不需要 utf-8 或其他任何与 ASCII 不同的东西并且对理解编码和 unicode 的目的不感兴趣的人来说,这是一个完美的答案。您可以将其用作 utf-8,因为与所有 ASCII 字符等效的 utf-8 字符是相同的(意味着将 ASCII 文件转换为 utf-8 文件会产生相同的文件(如果它没有 BOM))。对于所有在文本中包含非 ASCII 字符的人来说,这个答案只是错误和误导性的。
109赞 ForNeVeR 10/5/2015 #5

到目前为止,正确的方法是使用 @Roman Kuzmin 在对 @M. Dudley 的评论中推荐的解决方案:

[IO.File]::WriteAllLines($filename, $content)

(我还通过剥离不必要的系统命名空间说明来缩短它 - 默认情况下它会自动替换。

评论

4赞 Liam 6/17/2016
这(无论出于何种原因)并没有为我删除 BOM,正如公认的答案一样
1赞 ForNeVeR 6/17/2016
@Liam,可能是一些旧版本的 PowerShell 或 .NET?
2赞 codewario 1/24/2017
我相信旧版本的 .NET WriteAllLines 函数默认写入了 BOM。所以这可能是一个版本问题。
3赞 chazbot7 10/31/2017
在 Powershell 3 中使用 BOM 进行写入确认,但在 Powershell 4 中没有 BOM 进行确认。我不得不使用达德利先生的原始答案。
6赞 Johny Skovdal 1/12/2018
因此,它适用于默认安装的 Windows 10。:)此外,建议的改进:[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
61赞 mklement0 1/24/2016 #6

注意:此答案适用于 Windows PowerShell;相比之下,在跨平台 PowerShell Core 版本 (v6+) 中,不带 BOM 的 UTF-8 是所有 cmdlet 的默认编码

  • 换言之:如果使用的是 PowerShell [Core] 版本 6 或更高版本则默认情况下会获得无 BOM 的 UTF-8 文件(也可以使用 / 显式请求,而使用 ) 获取 with-BOM 编码。-Encoding utf8-Encoding utf8NoBOM-utf8BOM

  • 如果您运行的是 Windows 10 或更高版本,并且您愿意在系统范围内切换到无 BOM 的 UTF-8 编码(但是,这会产生深远的影响),甚至可以使 Windows PowerShell 始终如一地使用无 BOM 的 UTF-8 - 请参阅此答案


为了补充 M. Dudley 自己简单而务实的回答(以及 ForNeVeR 更简洁的重新表述):

  • 一个简单的(非流式处理)PowerShell 本机替代方法是使用 New-Item,它(奇怪地)默认创建无 BOM 的 UTF-8 文件,即使在 Windows PowerShell 中也是如此:

    # Note the use of -Raw to read the file as a whole.
    # Unlike with Set-Content / Out-File *no* trailing newline is appended.
    $null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
    
    • 注意:要以与将相同的格式保存任意命令的输出,请先通过管道传输到 Out-String;例如:Out-File

       $null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String) 
      
  • 为方便起见,下面是高级自定义函数 Out-FileUtf8NoBom,这是一种基于管道的替代方法,可模拟 Out-File这意味着:

    • 您可以像在管道中一样使用它。Out-File
    • 非字符串的输入对象的格式与将它们发送到控制台时的格式相同,就像使用 一样。Out-File
    • 一个附加开关允许您使用 Unix 格式的仅 LF 换行符 () 而不是您通常获得的 Windows 格式 CRLF 换行符 ()。-UseLF"`n""`r`n"

例:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines

请注意 (Get-Content $MyPath) 是如何包含在 (...) 中的,这可确保在通过管道发送结果之前打开、完整读取和关闭整个文件。这是必要的,以便能够写回同一文件(就地更新它)。
但是,通常不建议使用这种技术,原因有 2 个:(a) 整个文件必须适合内存,以及 (b) 如果命令中断,数据将丢失。

关于内存使用的注意事项:

  • M. Dudley 自己的答案和上面的替代方案要求首先在内存中构建整个文件内容,这对于大型输入集来说可能会有问题。New-Item
  • 下面的函数不需要这样做,因为它是作为代理(包装器)函数实现的(有关如何定义此类函数的简明摘要,请参阅此答案)。

函数Out-FileUtf8NoBom的源代码

注意:该函数可作为 MIT 许可的 Gist 使用,并且以后只会保留后者。

您可以使用以下命令直接安装它(虽然我个人可以向您保证这样做是安全的,但在以这种方式直接执行脚本之前,您应该始终检查脚本的内容):

# Download and define the function.
irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex
function Out-FileUtf8NoBom {

  <#
  .SYNOPSIS
    Outputs to a UTF-8-encoded file *without a BOM* (byte-order mark).

  .DESCRIPTION

    Mimics the most important aspects of Out-File:
      * Input objects are sent to Out-String first.
      * -Append allows you to append to an existing file, -NoClobber prevents
        overwriting of an existing file.
      * -Width allows you to specify the line width for the text representations
        of input objects that aren't strings.
    However, it is not a complete implementation of all Out-File parameters:
      * Only a literal output path is supported, and only as a parameter.
      * -Force is not supported.
      * Conversely, an extra -UseLF switch is supported for using LF-only newlines.

  .NOTES
    The raison d'être for this advanced function is that Windows PowerShell
    lacks the ability to write UTF-8 files without a BOM: using -Encoding UTF8 
    invariably prepends a BOM.

    Copyright (c) 2017, 2022 Michael Klement <[email protected]> (http://same2u.net), 
    released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).

  #>

  [CmdletBinding(PositionalBinding=$false)]
  param(
    [Parameter(Mandatory, Position = 0)] [string] $LiteralPath,
    [switch] $Append,
    [switch] $NoClobber,
    [AllowNull()] [int] $Width,
    [switch] $UseLF,
    [Parameter(ValueFromPipeline)] $InputObject
  )

  begin {

    # Convert the input path to a full one, since .NET's working dir. usually
    # differs from PowerShell's.
    $dir = Split-Path -LiteralPath $LiteralPath
    if ($dir) { $dir = Convert-Path -ErrorAction Stop -LiteralPath $dir } else { $dir = $pwd.ProviderPath }
    $LiteralPath = [IO.Path]::Combine($dir, [IO.Path]::GetFileName($LiteralPath))
    
    # If -NoClobber was specified, throw an exception if the target file already
    # exists.
    if ($NoClobber -and (Test-Path $LiteralPath)) {
      Throw [IO.IOException] "The file '$LiteralPath' already exists."
    }
    
    # Create a StreamWriter object.
    # Note that we take advantage of the fact that the StreamWriter class by default:
    # - uses UTF-8 encoding
    # - without a BOM.
    $sw = New-Object System.IO.StreamWriter $LiteralPath, $Append
    
    $htOutStringArgs = @{}
    if ($Width) { $htOutStringArgs += @{ Width = $Width } }

    try { 
      # Create the script block with the command to use in the steppable pipeline.
      $scriptCmd = { 
        & Microsoft.PowerShell.Utility\Out-String -Stream @htOutStringArgs | 
          . { process { if ($UseLF) { $sw.Write(($_ + "`n")) } else { $sw.WriteLine($_) } } }
      }  
      
      $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
      $steppablePipeline.Begin($PSCmdlet)
    }
    catch { throw }

  }

  process
  {
    $steppablePipeline.Process($_)
  }

  end {
    $steppablePipeline.End()
    $sw.Dispose()
  }

}

评论

1赞 nhooyr 12/15/2022
从 utf8BOM 文件到纯 utf8 的示例用法:$null = New-Item -Force "\$env:ProgramData\ssh\administrators_authorized_keys" -Value (Get-Content -Path "\$env:ProgramData\ssh\administrators_authorized_keys" | Out-String)
2赞 mklement0 12/15/2022
@nhooyr,最好使用(速度更快,并保留现有的换行符格式) - 我已经更新了答案。$null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
0赞 Erik Anderson 9/23/2016 #7

我使用的一种技术是使用 Out-File cmdlet 将输出重定向到 ASCII 文件。

例如,我经常运行 SQL 脚本,这些脚本创建另一个要在 Oracle 中执行的 SQL 脚本。使用简单的重定向 (“>”),输出将采用 SQLPlus 无法识别的 UTF-16 格式。要解决此问题,请执行以下操作:

sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force

然后,可以通过另一个 SQLPlus 会话执行生成的脚本,而无需担心任何 Unicode:

sqlplus / as sysdba "@new_script.sql" |
tee new_script.log

更新:正如其他人所指出的,这将删除非 ASCII 字符。由于用户要求一种“强制”转换的方法,我认为他们并不关心这一点,因为他们的数据可能不包含此类数据。

如果您关心非 ASCII 字符的保留,那么这不是您的答案。

评论

7赞 mklement0 2/20/2018
是的,避免了 BOM 问题,但您显然只支持 7 位 ASCII 字符。鉴于 ASCII 是 UTF-8 的子集,因此从技术上讲,生成的文件也是一个有效的 UTF-8 文件,但输入中的所有非 ASCII 字符都将转换为文字 字符-Encoding ASCII
1赞 Amit Naidu 3/8/2018
这个答案需要更多的选票。sqlplus 与 BOM 的不兼容是许多令人头疼的原因。
2赞 Joel Coehoorn 2/10/2022
@AmitNaidu 不,这是错误的答案,因为如果文本有任何非 ASCII 字符,它将不起作用:任何重音、变音符号、东方/克里特语等。
0赞 Erik Anderson 2/12/2022
@JoelCoehoorn 根据用户提出的问题,这是一个正确的答案。由于用户要求一种“强制”的方法,因此他们不希望出现任何问题或不在乎,因为源没有使用任何非 ASCII 字符。对于那些关心保留这些字符的人来说,这是行不通的。
1赞 Jaume Suñer Mut 10/3/2016 #8

将多个文件按扩展名更改为不带 BOM 的 UTF-8:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in ls -recurse -filter "*.java") {
    $MyFile = Get-Content $i.fullname 
    [System.IO.File]::WriteAllLines($i.fullname, $MyFile, $Utf8NoBomEncoding)
}
79赞 Lenny 12/2/2016 #9

我以为这不会是UTF,但我只是找到了一个非常简单的解决方案,似乎有效......

Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext

对我来说,无论源格式如何,这都会导致没有 bom 文件的 utf-8。

评论

18赞 Just Rudy 1/12/2017
这对我有用,除了我用于我的要求。-encoding utf8
1赞 user1529294 4/7/2017
谢谢。我正在处理一个工具的转储日志 - 里面有标签。UTF-8 不起作用。ASCII 解决了这个问题。谢谢。
74赞 mklement0 4/7/2017
是的,避免了 BOM 问题,但您显然只能获得 7 位 ASCII 字符。鉴于 ASCII 是 UTF-8 的子集,因此从技术上讲,生成的文件也是一个有效的 UTF-8 文件,但输入中的所有非 ASCII 字符都将转换为文字 字符-Encoding ASCII
10赞 ygoe 4/13/2022
警告:当然不是。这将删除所有非 ASCII 字符,并用问号替换它们。不要这样做,否则您将丢失数据!(在 Windows 5.1 上尝试使用 PS 10)
1赞 fourpastmidnight 9/7/2023
10000% 同意@ygoe。应避免使用此解决方案。这里有更好的答案,例如公认的答案 - 当然,默认情况下,较新版本的 x-plat PowerShell 不使用 BOM;但对于使用桌面版的用户,请参阅接受的答案。
1赞 frank tan 2/8/2017 #10
    [System.IO.FileInfo] $file = Get-Item -Path $FilePath 
    $sequenceBOM = New-Object System.Byte[] 3 
    $reader = $file.OpenRead() 
    $bytesRead = $reader.Read($sequenceBOM, 0, 3) 
    $reader.Dispose() 
    #A UTF-8+BOM string will start with the three following bytes. Hex: 0xEF0xBB0xBF, Decimal: 239 187 191 
    if ($bytesRead -eq 3 -and $sequenceBOM[0] -eq 239 -and $sequenceBOM[1] -eq 187 -and $sequenceBOM[2] -eq 191) 
    { 
        $utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) 
        [System.IO.File]::WriteAllLines($FilePath, (Get-Content $FilePath), $utf8NoBomEncoding) 
        Write-Host "Remove UTF-8 BOM successfully" 
    } 
    Else 
    { 
        Write-Warning "Not UTF-8 BOM file" 
    }  

来源链接 如何使用 PowerShell 从文件中删除 UTF8 字节顺序标记 (BOM)

1赞 SATO Yusuke 5/24/2017 #11

如果要使用 ,则应将第二个参数强制转换为(如果 的类型为 ),并使用 指定绝对路径,如下所示:[System.IO.File]::WriteAllLines()String[]$MyFileObject[]$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Set-Variable MyFile
[System.IO.File]::WriteAllLines($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath), [String[]]$MyFile, $Utf8NoBomEncoding)

如果你想使用 ,有时你应该将第二个参数通过管道输送到每行的末尾,以显式地将 CRLF 添加到每行的末尾(尤其是当你将它们与 一起使用时):[System.IO.File]::WriteAllText()| Out-String |ConvertTo-Csv

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | Set-Variable tmp
[System.IO.File]::WriteAllText("/absolute/path/to/foobar.csv", $tmp, $Utf8NoBomEncoding)

或者您可以与以下一起使用:[Text.Encoding]::UTF8.GetBytes()Set-Content -Encoding Byte

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | % { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Encoding Byte -Path "/absolute/path/to/foobar.csv"

请参阅: 如何在没有 BOM 的情况下将 ConvertTo-Csv 的结果写入 UTF-8 文件

评论

0赞 mklement0 2/20/2018
好的指针;建议/:更简单的替代方法是;如果要确保尾随 CRLF,只需使用单个输入字符串即可(无需 )。$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)Convert-Path $MyPath[System.IO.File]::WriteAllLines()Out-String
20赞 Lucero 4/24/2018 #12

这是公认的答案的替代方法。

此方法的优点是它与对象(来自 Get-Item 等函数)和相对路径兼容。IO.FileInfo

  1. 创建对象Text.UTF8Encoding

    • 虽然能够插入 BOM,但默认情况下不能Text.UTF8Encoding
  2. 调用对象的方法将字符串转换为字节GetBytes

    • 确保目标字符串实际上不是字符串数组 - 应等于 1$stringVar.Count
  3. 将 the to your target withbyte arraySet-Content -Encoding Byte

# This is a reusable class instance object
$utf8 = New-Object Text.UTF8Encoding

$GCRaw = Get-Content -Raw -PSPath $MyPath
Set-Content -Value $utf8.GetBytes($GCRaw) -Encoding Byte -PSPath $MyPath

这可以通过通过位置推断以及从参数中创建对象来缩短。-ValueText.UTF8Encoding

$GCRaw = Get-Content $MyPath -Raw

Set-Content ([Text.UTF8Encoding]::new().GetBytes($GCRaw)) -Encoding Byte -PSPath $MyPath

#NOTE#
# (New-Object Text.UTF8Encoding).GetBytes($GCRaw))
# can be used instead of
# ([Text.UTF8Encoding]::new().GetBytes($GCRaw))
# For code intended to be compact, I recommend the latter,
# not just because it's not as long, but also because its
# lack of whitespace makes it visually more distinct.

评论

1赞 mklement0 10/26/2020
不错 - 适用于字符串(这可能是所有需要的,当然满足问题的要求)。如果您需要利用与 提供的格式不同,请先使用管道;例如,Out-FileSet-ContentOut-String$MyFile = Get-ChildItem | Out-String
32赞 sc911 3/9/2019 #13

版本 6 开始,powershell 支持 set-contentout-file 的编码,甚至将其用作默认编码。UTF8NoBOM

所以在上面的例子中,它应该是这样的:

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath

评论

5赞 KCD 10/29/2019
好。仅供参考,检查版本$PSVersionTable.PSVersion
1赞 mklement0 10/26/2020
值得注意的是,在 PowerShell [Core] 中,v6+ 从来都不是必需的,因为它是默认编码。-Encoding UTF8NoBOM
0赞 JGFMK 3/29/2023
learn.microsoft.com/en-us/powershell/module/......似乎证实了这一点 - 但是在抓取重定向的 DOS 输出到文件并发出回音时遇到问题。Notepad++ 在我发出的两个文件上提供了不同的编码(一个是 UCS-2 LE BOM,另一个是 UTF8-BOM!) - 当驱动屏幕抓取自动化/重定向到临时输出文件并提取子字符串时。我从 CSV 驱动同一命令的不同参数,并在任何地方设置编码 - 但 Powershell 似乎忽略了这一点。
0赞 JGFMK 3/29/2023
如果您对 Powershell 编码无法按预期工作感到困惑,请务必查看此答案和我在问题本身下的评论(尽管特别要求 utf8,但 Set-Content 和 Out-File 给出了不同的编码答案!stackoverflow.com/a/5596984/495157
3赞 Zombo 12/9/2020 #14

对于 PowerShell 5.1,请启用以下设置:

控制面板、区域、管理、更改系统区域设置、使用 Unicode UTF-8 提供全球语言支持

然后在 PowerShell 中输入以下内容:

$PSDefaultParameterValues['*:Encoding'] = 'Default'

或者,可以升级到 PowerShell 6 或更高版本。

https://github.com/PowerShell/PowerShell

评论

1赞 mklement0 12/9/2020
说明一下:这是一个系统范围的设置,它使 Windows PowerShell 在所有 cmdlet 中默认为无 BOM 的 UTF-8,这可能是也不需要的,尤其是因为该功能仍处于测试阶段(在撰写本文时),并且可能会破坏旧版控制台应用程序 - 有关背景信息,请参阅此答案
7赞 Andreas Covidiot 1/29/2021 #15

important!:仅当开头的额外空格或换行符对于文件的用例没有问题时(例如,如果它是 SQL 文件、Java 文件或人类可读的文本文件),这才有效

可以结合创建一个空文件(非 UTF8 或 ASCII(兼容 UTF8))并附加到它(如果源文件是文件,则替换为):$strgc $src

" "    |  out-file  -encoding ASCII  -noNewline  $dest
$str  |  out-file  -encoding UTF8   -append     $dest

作为单行

替换并根据您的用例:$dest$str

$_ofdst = $dest ; " " | out-file -encoding ASCII -noNewline $_ofdst ; $src | out-file -encoding UTF8 -append $_ofdst

作为简单的函数

function Out-File-UTF8-noBOM { param( $str, $dest )
  " "    |  out-file  -encoding ASCII  -noNewline  $dest
  $str  |  out-file  -encoding UTF8   -append     $dest
}

将其与源文件一起使用:

Out-File-UTF8-noBOM  (gc $src),  $dest

将其与字符串一起使用:

Out-File-UTF8-noBOM  $str,  $dest
  • 可选:继续附加:Out-File

    "more foo bar"  |  Out-File -encoding UTF8 -append  $dest
    

评论

0赞 fourpastmidnight 9/7/2023
UTF8 != ASCII。所以不,这并非在所有情况下都有效。所有 ASCII 都可以转换为 UTF8,但并非所有 UTF8 都可以转换为 ASCII 并再次转换回来——即 UTF8 不能(全部)往返到 ASCII 并返回。事实上,这可能很危险。
0赞 Andreas Covidiot 9/8/2023
@fourpastmidnight这就是为什么我在开头提到这一点:)
6赞 JensG 3/24/2021 #16

老问题,新答案:

虽然“旧”powershell 写入 BOM,但新的与平台无关的变体的行为确实不同:默认值为“无 BOM”,可以通过交换机进行配置:

-编码

指定目标文件的编码类型。默认值为 utf8NoBOM。

此参数的可接受值如下所示:

  • ascii:使用 ASCII(7 位)字符集的编码。
  • bigendianunicode:使用 big-endian 字节顺序以 UTF-16 格式编码。
  • oem:使用 MS-DOS 和控制台程序的默认编码。
  • unicode:使用 little-endian 字节顺序以 UTF-16 格式编码。
  • utf7:以 UTF-7 格式编码。
  • utf8:以 UTF-8 格式编码。
  • utf8BOM:以 UTF-8 格式编码,带有字节顺序标记 (BOM)
  • utf8NoBOM:以 UTF-8 格式编码,不带字节顺序标记 (BOM)
  • utf32:以 UTF-32 格式编码。

来源:https://learn.microsoft.com/de-de/powershell/module/Microsoft.PowerShell.Utility/Out-File?view=powershell-7 Emphasis mine

1赞 Nader Gharibian Fard 2/2/2022 #17

我在 PowerShell 中遇到了同样的错误,并使用了这种隔离并修复了它

$PSDefaultParameterValues['*:Encoding'] = 'utf8'

评论

0赞 LPChip 9/11/2023
仅仅设置 utf 是不够的。您还需要指定 -encoding default。例如:。请注意,这会使用 utf8 对文件进行重新编码,因此未正确编码的文件将更改。$filecontent | Out-File $Filename -Encoding default
0赞 Tanmay Sarin 5/26/2022 #18

使用此方法编辑 UTF8-NoBOM 文件并生成编码正确的文件 -

$fileD = "file.xml"
(Get-Content $fileD) | ForEach-Object { $_ -replace 'replace text',"new text" } | out-file "file.xml" -encoding ASCII

起初我对这种方法持怀疑态度,但它让我感到惊讶并奏效了!

使用 powershell 版本 5.1 进行测试

2赞 Pravanjan Hota 6/23/2022 #19

我会说只使用 Set-Content 命令,不需要其他命令。

我系统中的powershell版本是:-

PS C:\Users\XXXXX> $PSVersionTable.PSVersion | fl


Major         : 5
Minor         : 1
Build         : 19041
Revision      : 1682
MajorRevision : 0
MinorRevision : 1682

PS C:\Users\XXXXX>

所以你需要类似的东西。

PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt | Set-Content .\Downloads\anotherfile.txt
PS C:\Users\XXXXX> Get-Content .\Downloads\anotherfile.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX>

现在,当我们根据屏幕截图检查文件时,它是utf8。另一个文件:.txt

PS:回答有关外来字符问题的评论查询。使用以下命令将具有外来字符的文件“testfgnchar.txt”中的内容复制到“findfnchar2.txt”。

PS C:\Users\XXXXX> Get-Content .\testfgnchar.txt | Set-Content findfnchar2.txt
PS C:\Users\XXXXX>

屏幕截图在这里

注意:目前,存在比我在回答时使用的 PowerShell 更新的版本。

评论

0赞 Chortos-2 3/21/2023
这起初似乎有效,但这实际上使用了用户的 ANSI 代码页,并用最接近的等价物(例如 š → s)或问号替换了其他符号。使用作品。set-content -encoding utf8
0赞 Pravanjan Hota 4/5/2023
@Chortos-2,感谢您的评论。我更关心严格地以“UTF-8”格式保存文件。如果我使用“set-content -encoding utf8”,它会以“UTF-8-BOM”格式保存文件
1赞 Chortos-2 4/6/2023
啊,这是真的;我没有注意到。但这意味着这个命令完全不适合这个任务,因为没有 ,它根本不使用 UTF-8无论是带 BOM 还是没有。-encoding
0赞 Pravanjan Hota 4/7/2023
@Chortos-2,每个系统或用户在安装过程中都会设置自己的语言。我分享的答案是针对我的系统上的语言英语(美国),并且在我的区域语言中也显示相同。我觉得这种语言在安装过程中最常用。因此,这可能是这里的障碍,但无济于事。您的系统中显示的是什么语言系统?按计算机上的 Windows 键和 I 找出相同的结果。
1赞 Chortos-2 4/9/2023
关键是此命令使用 ANSI 代码页,而不是问题中明确请求的 UTF-8(除非您根据 Zombo 的答案将 ANSI 设置为 UTF-8)。在您的英语系统上,尝试 ,您将看到没有保留任何字符。在其他答案的注释中,针对其他 PowerShell 命令指出了相同的问题。知道默认以单个字节编码拉丁语当然是件好事,但它与最初请求的 UTF-8 有很大不同。echo āčķʃλшא⁴ℝ→⅛≈あ子 | set-content file.txtset-content
0赞 LPChip 9/11/2023 #20

如果您的第一行不包含任何不需要 UTF8 的花哨内容,则以下内容将在库存 Windows 10 Powershell 上创建一个没有 BOM 的 UTF8 文件:

$file = get-content -path "C:\temp\myfile.txt" -Encoding UTF8

# do some stuff.

$file[0] | out-file "C:\temp\mynewfile.txt" -Encoding ascii
$file | select -skip 1 | out-file "C:\temp\mynewfile.txt" -append utf8

这将使用 2 行来创建新文件。第一个使用 -encoding ascii 来强制 UTF8,但它将限制为 7 位 ascii。对于文本文件,这通常不是问题,否则您可能会选择字节编码。

第二个命令附加其余部分,但跳过第一行,因为我们已经解析了具有完全 UTF8 支持的该行。