Использование PowerShell для записи файла в UTF-8 без спецификации - PullRequest
216 голосов
/ 08 апреля 2011

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

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

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

Ответы [ 15 ]

201 голосов
/ 08 апреля 2011

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

$MyFile = Get-Content $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)
67 голосов
/ 05 октября 2015

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

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

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

38 голосов
/ 02 декабря 2016

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

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

Для меня это приводит к UTF-8 без файла BOM, независимо отисходный формат.

26 голосов
/ 24 января 2016

Примечание. Этот ответ относится к Windows PowerShell ; напротив, в кроссплатформенной версии PowerShell Core UTF-8 без спецификации является кодировкой по умолчанию .

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

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

  • вы можете использовать его так же, как Out-File в конвейере.
  • входные объекты, которые не являются строками, форматируются так, как если бы вы отправляли их на консоль, точно так же как с Out-File.
* +1032 * Пример: * 1 033 *
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath

Обратите внимание, как (Get-Content $MyPath) заключен в (...), что гарантирует, что весь файл будет открыт, прочитан полностью и закрыт перед отправкой результата по конвейеру. Это необходимо для возможности обратной записи в тот же файл (обновите его вместо ).
Однако, как правило, этот метод не рекомендуется по двум причинам: (а) весь файл должен уместиться в памяти и (б) если команда прервана, данные будут потеряны.

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

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

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

<#
.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 doesn't 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()
  }

}
8 голосов
/ 23 апреля 2018

При использовании 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() или аналогичной заключается в том, что он должен нормально работать с любым типом элемента и пути, а не только фактические пути к файлам.

5 голосов
/ 09 марта 2019

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

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

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
4 голосов
/ 01 мая 2013

Этот скрипт преобразует в 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);
}
1 голос
/ 08 февраля 2017
    [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

0 голосов
/ 19 июля 2018

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

$MyFile | Out-File -Encoding Oem $MyPath

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

0 голосов
/ 24 мая 2017

Если вы хотите использовать [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(), иногда вам следует передать второй параметр в | 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 без спецификации

...