Использование PowerShell для записи файла в UTF-8 без спецификации

Out-File похоже, заставляет спецификацию использовать UTF-8:

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

Как я могу написать файл в UTF-8 без спецификации с помощью PowerShell?

Ответ 1

Использование .NET UTF8Encoding класс и передача $False в конструктор, кажется, работает:

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

Ответ 2

правильный способ использовать решение, рекомендованное @Roman Kuzmin в комментариях до @M. Dudley answer:

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

(Я также немного сократил это, сняв ненужное пояснение пространства имен System - по умолчанию оно будет заменено автоматически.)

Ответ 3

Я понял, что это не будет UTF, но я просто нашел довольно простое решение, которое, похоже, работает...

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

Для меня это приводит к utf-8 без bom файла независимо от исходного формата.

Ответ 4

Note: This answer applies to Windows PowerShell; by contrast, in the cross-platform PowerShell Core edition, UTF-8 without BOM is the default encoding.

Чтобы дополнить М. Дадли собственный простой и прагматичный ответForNeVeR более краткая переформулировка):

Для удобства здесь используется расширенная функция Out-FileUtf8NoBom, альтернатива на основе конвейера, которая имитирует Out-File, что означает:

  • вы можете использовать его так же, как Out-File в конвейере.
  • входные объекты, которые не являются строками, форматируются так, как если бы вы отправляли их на консоль, как в Out-File.

Пример:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath

Note how [TG45] is enclosed in [TG46], which ensures that the entire file is opened, read in full, and closed before sending the result through the pipeline. This is necessary in order to be able to write back to the same file (update it in place).
Generally, though, this technique is not advisable for 2 reasons: (a) the whole file must fit into memory and (b) if the command is interrupted, data will be lost.

Примечание об использовании памяти memory use:

  • Собственный ответ м. Дадли требует, чтобы все содержимое файла сначала создавалось в памяти, что может быть проблематично для больших файлов.
  • Приведенная ниже функция улучшает это лишь незначительно: все входные объекты по-прежнему буферизуются в первую очередь, но их строковые представления затем генерируются и записываются в выходной файл один за другим.

Исходный код Out-FileUtf8NoBom (также доступен как Gist с лицензией MIT):

<#
.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-String parameters:
  * Only a literal output path is supported, and only as a parameter.
  * -Force is not supported.

  Caveat: *All* pipeline input is buffered before writing output starts,
          but the string representations are generated and written to the target
          file one by one.

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

#>
function Out-FileUtf8NoBom {

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

  #requires -version 3

  # Make sure that the .NET framework sees the same working dir. as PS
  # and resolve the input path to a full path.
  [System.IO.Directory]::SetCurrentDirectory($PWD) # Caveat: .NET Core does not support [Environment]::CurrentDirectory
  $LiteralPath = [IO.Path]::GetFullPath($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 IO.StreamWriter $LiteralPath, $Append

  $htOutStringArgs = @{}
  if ($Width) {
    $htOutStringArgs += @{ Width = $Width }
  }

  # Note: By not using begin / process / end blocks, we're effectively running
  #       in the end block, which means that all pipeline input has already
  #       been collected in automatic variable $Input.
  #       We must use this approach, because using | Out-String individually
  #       in each iteration of a process block would format each input object
  #       with an indvidual header.
  try {
    $Input | Out-String -Stream @htOutStringArgs | % { $sw.WriteLine($_) }
  } finally {
    $sw.Dispose()
  }

}

Ответ 5

Начиная с версии 6 powershell поддерживает кодировку UTF8NoBOM для set-content и out-file и даже использует ее в качестве кодировки по умолчанию.

Так что в приведенном выше примере это должно быть просто так:

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath

Ответ 6

При использовании Set-Content вместо Out-File вы можете указать кодировку Byte, которая может использоваться для записи байтового массива в файл. Это в сочетании с пользовательской кодировкой UTF8, которая не излучает спецификацию, дает желаемый результат:

# This variable can be reused
$utf8 = New-Object System.Text.UTF8Encoding $false

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

Разница в использовании [IO.File]::WriteAllLines() или аналогичного заключается в том, что он должен работать нормально с любым типом элемента и пути, а не только с фактическими путями файлов.

Ответ 7

Этот script преобразует в UTF-8 без спецификации все .txt файлы в DIRECTORY1 и выводит их в DIRECTORY2

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

Ответ 8

    [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" 
    }  

Источник Как удалить метку заказа байта UTF8 (BOM) из файла с помощью PowerShell

Ответ 9

Если вы хотите использовать [System.IO.File]::WriteAllLines(), вы должны [System.IO.File]::WriteAllLines() второй параметр в String[] (если тип $MyFile - Object[]), а также указать абсолютный путь с помощью $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)

Если вы хотите использовать [System.IO.File]::WriteAllText(), иногда вам нужно [System.IO.File]::WriteAllText() второй параметр в | Out-String | | Out-String | чтобы добавить CRLF в конец каждой строки (особенно, когда вы используете их с 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"

см.: Как написать результат ConvertTo-Csv в файл в UTF-8 без спецификации

Ответ 10

Один из методов, который я использую, - перенаправить вывод в файл ASCII с помощью командлета Out-File.

Например, я часто запускаю сценарии SQL, которые создают другой SQL script для выполнения в Oracle. При простом перенаправлении ( " > " ) вывод будет в UTF-16, который не распознается SQLPlus. Чтобы обойти это:

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

Сгенерированный script может быть выполнен через другой сеанс SQLPlus без каких-либо проблем с Unicode:

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

Ответ 11

Измените несколько файлов по расширению на 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)
}

Ответ 12

По какой-то причине вызовы WriteAllLines все еще создавали для меня спецификацию с аргументом BOMless UTF8Encoding и без него. Но для меня работало следующее:

$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])

Мне пришлось сделать путь к файлу абсолютным, чтобы он работал. В противном случае он написал файл на моем рабочем столе. Кроме того, я полагаю, это работает, только если вы знаете, что ваша спецификация составляет 3 байта. Я не знаю, насколько надежно ожидать определенный формат/длину спецификации на основе кодирования.

Кроме того, как написано, это, вероятно, работает только в том случае, если ваш файл вписывается в массив powershell, который, как представляется, имеет ограничение на длину некоторого значения ниже, чем [int32]::MaxValue на моей машине.

Ответ 13

Этот работает для меня (используйте "Default" вместо "UTF8" ):

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

В результате получается ASCII без спецификации.

Ответ 14

Можно использовать ниже, чтобы получить UTF8 без спецификации

$MyFile | Out-File -Encoding ASCII

Ответ 15

Была такая же проблема. Это помогло мне:

$MyFile | Out-File -Encoding Oem $MyPath

При открытии файла с кодом Visual Studio или Notepad++ он отображается как UTF-8

В конечном итоге это просто "появиться" на работе. При открытии в каком-либо редакторе он отображается как UTF-8 без спецификации. Но это совсем не так. Используйте раствор в верхней части протектора. Эта работа по-настоящему