Out-File -Encoding с помощью команды PowerShell replace [duplicate]

Интересно, что ни один из ответов на этой странице не упоминает два крайних случая, надеюсь, никто не возражает, если я их добавлю:

Случай с краем # 1: одновременный доступ к Словарю

Родовые словари в .NET не являются потокобезопасными, а иногда могут бросать NullReference или даже (чаще) a KeyNotFoundException при попытке получить доступ к ключу из двух параллельных потоков. Исключение в этом случае является довольно ошибочным.

Случай с краем # 2: небезопасный код

Если код NullReferenceException задан кодом unsafe, вы можете посмотреть на переменные указателя , и проверьте их на IntPtr.Zero или что-то в этом роде. Это одно и то же («исключение нулевого указателя»), но в небезопасном коде переменные часто переводятся в типы значений / массивы и т. Д., И вы ударяете головой о стену, задаваясь вопросом, как тип значения может исключение.

(Еще одна причина для небезопасного использования небезопасного кода, если вам это нужно)

181
задан M. Dudley 8 April 2011 в 16:02
поделиться

14 ответов

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

$MyFile = Get-Content $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)
167
ответ дан Nathan Tuggy 27 August 2018 в 21:52
поделиться

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

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

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

57
ответ дан Community 27 August 2018 в 21:52
поделиться

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

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

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
1
ответ дан Erik Anderson 27 August 2018 в 21:52
поделиться
    [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
ответ дан frank tan 27 August 2018 в 21:52
поделиться

Этот скрипт преобразует в 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);
}
4
ответ дан jamhan 27 August 2018 в 21:52
поделиться

Измените несколько файлов по расширению на 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)
}
0
ответ дан Jaume Suñer Mut 27 August 2018 в 21:52
поделиться

Это работает для меня (используйте «По умолчанию» вместо «UTF8»):

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

Результатом является ASCII без спецификации.

-3
ответ дан Krzysztof 27 August 2018 в 21:52
поделиться

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

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

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

31
ответ дан Lenny 27 August 2018 в 21:52
поделиться

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

3
ответ дан Lucero 27 August 2018 в 21:52
поделиться

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

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

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

Пример:

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

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

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

  • 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()
  }

}
22
ответ дан mklement0 27 August 2018 в 21:52
поделиться

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

$MyFile | Out-File -Encoding ASCII
-1
ответ дан Robin Wang 27 August 2018 в 21:52
поделиться

Если вы хотите использовать [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 без спецификации

0
ответ дан satob 27 August 2018 в 21:52
поделиться

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

$MyFile | Out-File -Encoding Oem $MyPath

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

1
ответ дан Woodrow Barlow 27 August 2018 в 21:52
поделиться

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

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

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

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

0
ответ дан xdhmoore 27 August 2018 в 21:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: