Out-File
tampaknya memaksa BOM saat menggunakan UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Bagaimana saya bisa menulis file di UTF-8 tanpa BOM menggunakan PowerShell?
Out-File
tampaknya memaksa BOM saat menggunakan UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Bagaimana saya bisa menulis file di UTF-8 tanpa BOM menggunakan PowerShell?
Jawaban:
Menggunakan UTF8Encoding
kelas .NET dan meneruskan $False
ke konstruktor tampaknya berfungsi:
$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
[System.IO.File]::WriteAllLines($MyPath, $MyFile)
sudah cukup. WriteAllLines
Kelebihan ini menulis persis UTF8 tanpa BOM.
WriteAllLines
tampaknya $MyPath
harus mutlak.
WriteAllLines
mendapatkan direktori saat ini dari [System.Environment]::CurrentDirectory
. Jika Anda membuka PowerShell dan kemudian mengubah direktori Anda saat ini (menggunakan cd
atau Set-Location
), maka [System.Environment]::CurrentDirectory
tidak akan berubah dan file akan berakhir di direktori yang salah. Anda dapat mengatasi ini dengan [System.Environment]::CurrentDirectory = (Get-Location).Path
.
Cara yang tepat seperti sekarang adalah menggunakan solusi yang direkomendasikan oleh @Roman Kuzmin dalam komentar ke @M. Dudley menjawab :
[IO.File]::WriteAllLines($filename, $content)
(Saya juga telah mempersingkatnya sedikit dengan menghapus System
klarifikasi namespace yang tidak perlu - itu akan diganti secara otomatis secara default.)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Saya pikir ini bukan UTF, tapi saya baru saja menemukan solusi yang cukup sederhana yang sepertinya berfungsi ...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
Bagi saya ini menghasilkan utf-8 tanpa file bom terlepas dari format sumbernya.
-encoding utf8
untuk kebutuhan saya.
-Encoding ASCII
hindari masalah BOM, tetapi Anda jelas hanya mendapatkan karakter ASCII 7-bit . Karena ASCII adalah bagian dari UTF-8, file yang dihasilkan secara teknis juga merupakan file UTF-8 yang valid, tetapi semua karakter non-ASCII dalam input Anda akan dikonversi ke ?
karakter literal .
-encoding utf8
masih menampilkan UTF-8 dengan BOM. :(
Catatan: Jawaban ini berlaku untuk Windows PowerShell ; Sebaliknya, dalam PowerShell Core edisi lintas platform (v6 +), UTF-8 tanpa BOM adalah penyandian default , di semua cmdlet.
Dengan kata lain: Jika Anda menggunakan PowerShell [Core] versi 6 atau lebih tinggi , Anda mendapatkan file UTF-8 BOM-kurang secara default (yang Anda juga dapat secara eksplisit meminta dengan -Encoding utf8
/ -Encoding utf8NoBOM
, sedangkan Anda mendapatkan dengan -BOM encoding dengan -utf8BOM
).
Untuk melengkapi jawaban M. Dudley yang sederhana dan pragmatis (dan reformulasi ForNeVeR yang lebih ringkas ):
Untuk kenyamanan, inilah fungsi lanjutan Out-FileUtf8NoBom
, alternatif berbasis pipa yang meniruOut-File
, yang berarti:
Out-File
dalam pipa.Out-File
.Contoh:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
Perhatikan bagaimana (Get-Content $MyPath)
terlampir (...)
, yang memastikan bahwa seluruh file dibuka, dibaca secara penuh, dan ditutup sebelum mengirim hasilnya melalui pipa. Ini diperlukan untuk dapat menulis kembali ke file yang sama (perbarui di tempat ).
Namun, secara umum, teknik ini tidak dianjurkan karena 2 alasan: (a) seluruh file harus sesuai dengan memori dan (b) jika perintah terputus, data akan hilang.
Catatan tentang penggunaan memori :
Kode sumberOut-FileUtf8NoBom
(juga tersedia sebagai Inti berlisensi 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.ProviderPath) # Caveat: Older .NET Core versions don'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()
}
}
Mulai dari versi 6 PowerShell mendukung UTF8NoBOM
encoding baik untuk set-content dan out-file dan bahkan menggunakan ini sebagai encoding default.
Jadi dalam contoh di atas seharusnya menjadi seperti ini:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
Saat menggunakan Set-Content
alih-alih Out-File
, Anda dapat menentukan pengkodean Byte
, yang dapat digunakan untuk menulis array byte ke file. Ini dikombinasikan dengan pengkodean UTF8 khusus yang tidak memancarkan BOM memberikan hasil yang diinginkan:
# 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
Perbedaan menggunakan [IO.File]::WriteAllLines()
atau serupa adalah bahwa itu harus berfungsi dengan baik dengan semua jenis item dan jalur, tidak hanya jalur file yang sebenarnya.
Skrip ini akan mengonversi, menjadi UTF-8 tanpa BOM, semua file .txt dalam DIRECTORY1 dan menghasilkannya menjadi DIRECTORY2
foreach ($i in ls -name DIRECTORY1\*.txt)
{
$file_content = Get-Content "DIRECTORY1\$i";
[System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
[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"
}
Sumber Cara menghapus UTF8 Byte Order Mark (BOM) dari file menggunakan PowerShell
Jika Anda ingin menggunakan [System.IO.File]::WriteAllLines()
, Anda harus memberikan parameter kedua ke String[]
(jika tipe $MyFile
is Object[]
), dan juga menentukan path absolut dengan $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
, seperti:
$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)
Jika Anda ingin menggunakan [System.IO.File]::WriteAllText()
, kadang-kadang Anda harus memasukkan parameter kedua ke dalam | Out-String |
untuk menambahkan CRLF ke akhir setiap baris secara eksplisit (Terutama ketika Anda menggunakannya 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)
Atau Anda dapat menggunakan [Text.Encoding]::UTF8.GetBytes()
dengan 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"
lihat: Cara menulis hasil ConvertTo-Csv ke file di UTF-8 tanpa BOM
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
adalah Convert-Path $MyPath
; jika Anda ingin memastikan CRLF tertinggal, cukup gunakan [System.IO.File]::WriteAllLines()
bahkan dengan string input tunggal (tidak perlu untuk Out-String
).
Salah satu teknik yang saya gunakan adalah untuk mengarahkan output ke file ASCII menggunakan cmdlet Out-File .
Sebagai contoh, saya sering menjalankan skrip SQL yang membuat skrip SQL lain untuk dieksekusi di Oracle. Dengan pengalihan sederhana (">"), output akan berada di UTF-16 yang tidak dikenali oleh SQLPlus. Untuk mengatasi ini:
sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force
Script yang dihasilkan kemudian dapat dieksekusi melalui sesi SQLPlus lain tanpa kekhawatiran Unicode:
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
-Encoding ASCII
hindari masalah BOM, tetapi Anda jelas hanya mendapatkan dukungan untuk karakter ASCII 7-bit . Karena ASCII adalah bagian dari UTF-8, file yang dihasilkan secara teknis juga merupakan file UTF-8 yang valid, tetapi semua karakter non-ASCII dalam input Anda akan dikonversi ke ?
karakter literal .
Ubah beberapa file dengan ekstensi menjadi UTF-8 tanpa BOM:
$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)
}
Untuk alasan apa pun, WriteAllLines
telepon masih menghasilkan BOM untuk saya, dengan UTF8Encoding
argumen BOMless dan tanpa itu. Tetapi yang berikut ini berhasil untuk saya:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
Saya harus membuat path file mutlak untuk berfungsi. Kalau tidak, ia menulis file ke Desktop saya. Juga, saya kira ini hanya berfungsi jika Anda tahu BOM Anda adalah 3 byte. Saya tidak tahu seberapa dapat diandalkan untuk mengharapkan format BOM yang diberikan / panjang berdasarkan pengkodean.
Juga, seperti yang ditulis, ini mungkin hanya berfungsi jika file Anda cocok dengan array powershell, yang tampaknya memiliki batas panjang beberapa nilai lebih rendah daripada [int32]::MaxValue
di komputer saya.
WriteAllLines
tanpa argumen penyandian tidak pernah menulis BOM itu sendiri , tetapi mungkin string Anda dimulai dengan BOM karakter ( U+FEFF
), yang pada penulisan secara efektif membuat BOM UTF-8; misalnya: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(hilangkan [char] 0xfeff +
untuk melihat bahwa tidak ada BOM yang ditulis).
[Environment]::CurrentDirectory = $PWD.ProviderPath
, atau, sebagai alternatif yang lebih umum untuk "$(pwd)\..."
pendekatan Anda (lebih baik:, "$pwd\..."
bahkan lebih baik: "$($pwd.ProviderPath)\..."
atau (Join-Path $pwd.ProviderPath ...)
), gunakan(Convert-Path BOMthetorpedoes.txt)
U+FEFF
abstrak tunggal .
Dapat menggunakan di bawah ini untuk mendapatkan UTF8 tanpa BOM
$MyFile | Out-File -Encoding ASCII
ASCII
bukan UTF-8, tetapi itu bukan codepage ANSI saat ini - Anda sedang memikirkan Default
; ASCII
benar-benar adalah pengkodean ASCII 7-bit, dengan codepoints = = 128 yang dikonversi ke ?
instance literal .
-Encoding ASCII
memang hanya ASCII 7-bit: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- ä
telah ditransliterasikan ke a ?
. Sebaliknya, -Encoding Default
("ANSI") akan melestarikannya dengan benar.
Ini berfungsi untuk saya (gunakan "Default" dan bukan "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
Hasilnya adalah ASCII tanpa BOM.
Default
pengkodean akan menggunakan halaman kode ANSI sistem saat ini, yang bukan UTF-8, seperti yang saya perlukan.