web-dev-qa-db-ja.com

PowerShellを使用してBOMなしでファイルをUTF-8で書き込む

UTF-8を使用している場合、Out-FileはBOMを強制するようです。

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

PowerShellを使用してBOMなしでファイルをUTF-8で書き込む方法はありますか。

213
M. Dudley

.NETの UTF8Encoding クラスを使用して$Falseをコンストラクタに渡すとうまくいくようです:

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

今のところ正しい方法は@Roman Kuzminが推奨する解決策を使うことです コメントで to @M。ダドリー 答え

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

(また、不要なSystem名前空間の説明を削除することで、少し短くしました。デフォルトで自動的に置き換えられます。)

67
ForNeVeR

私はこれがUTFではないと考えました、しかし私はちょうどうまくいくようであるかなり簡単な解決策を見つけました...

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

私にとってはこれはソースフォーマットに関係なくbomファイルなしのutf-8という結果になります。

37
Lenny

注:この回答は、Windows PowerShell;に適用されます。対照的に、クロスプラットフォームのPowerShellCoreエディションでは、BOMなしのUTF-8はですデフォルトのエンコード

M。ダドリー自身のシンプルで実用的な答え (および ForNeVeRのより簡潔な再定式化 )を補完するには:

便宜上、高度な関数Out-FileUtf8NoBomOut-Fileを模倣するパイプラインベースの代替手段を次に示します。

  • パイプラインでOut-Fileと同じように使用できます。
  • 文字列ではない入力オブジェクトは、Out-Fileと同様に、コンソールに送信した場合と同じようにフォーマットされます。

例:

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

(Get-Content $MyPath)(...)で囲まれていることに注意してください。これにより、ファイル全体が開かれ、完全に読み込まれ、結果がパイプラインを介して送信される前に閉じられます。これは、sameファイルに書き戻すことができるようにするために必要です(更新inplace)。
一般に、この手法は次の2つの理由からお勧めできません。(a)ファイル全体がメモリに収まる必要がある、(b)コマンドが中断された場合、データが失われる。

メモリ使用量に関するメモ

  • M。ダドリー自身の回答 では、ファイルの内容全体を最初にメモリに構築する必要がありますが、これは大きなファイルでは問題になる可能性があります。
  • 以下の関数は、これを少しだけ改善します。すべての入力オブジェクトが最初にバッファリングされますが、その文字列表現が生成され、出力ファイルに1つずつ書き込まれます。

Out-FileUtf8NoBomのソースコード(利用可能 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 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()
  }

}
26
mklement0

Set-Contentの代わりにOut-Fileを使用するときは、バイト配列をファイルに書き込むために使用できるエンコードByteを指定できます。これをBOMを発行しないカスタム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()などを使うこととの違いは、実際のファイルパスだけでなく、どんな種類のアイテムやパスでもうまく動作するはずだということです。

8
Lucero

このスクリプトは、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);
}
4
jamhan

バージョン6以降では、powershellは set-contentout-file の両方でUTF8NoBOMエンコーディングをサポートし、これをデフォルトのエンコーディングとしても使用します。

したがって、上記の例では、単純に次のようになります。

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
4
sc911

何らかの理由で、WriteAllLines呼び出しはまだBOMなしのUTF8Encoding引数を使用し、使用せずにBOMを作成していました。しかし、以下は私のために働いた:

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

それが機能するためには、ファイルパスを絶対パスにする必要がありました。そうでなければそれは私のデスクトップにファイルを書いた。また、これはBOMが3バイトであることがわかっている場合にのみ機能すると思います。エンコーディングに基づいて特定のBOMフォーマット/長さを予測することがどれほど信頼できるかわかりません。

また、書かれているように、これはおそらくあなたのファイルがpowershell配列に収まる場合にのみうまくいくでしょう。これは私のマシンでは[int32]::MaxValueよりも小さいいくつかの値の長さ制限があるようです。

0
xdhmoore
    [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を使用してファイルからUTF-8バイトオーダーマーク(BOM)を削除する方法

0
frank tan

私が利用しているテクニックの1つは、Out-Fileコマンドレットを使用して、出力をASCIIファイルにリダイレクトすることです。

たとえば、Oracleで実行するための別のSQLスクリプトを作成するSQLスクリプトをよく実行します。単純なリダイレクト( ">")では、出力はUTF-16になり、SQLPlusでは認識されません。これを回避するには

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

生成されたスクリプトは、Unicodeの心配なしに別のSQLPlusセッションを介して実行することができます。

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

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)
}
0

[System.IO.File]::WriteAllLines()を使用したい場合は、2番目のパラメータを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()を使いたい場合は、2行目のパラメータを| 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"

参照: BOMなしでConvertTo-Csvの結果をUTF-8でファイルに書き込む方法

0
SATO Yusuke