Out-File
ดูเหมือนว่าจะบังคับ BOM เมื่อใช้ UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
ฉันจะเขียนไฟล์ใน UTF-8 ที่ไม่มี BOM โดยใช้ PowerShell ได้อย่างไร
Out-File
ดูเหมือนว่าจะบังคับ BOM เมื่อใช้ UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
ฉันจะเขียนไฟล์ใน UTF-8 ที่ไม่มี BOM โดยใช้ PowerShell ได้อย่างไร
คำตอบ:
การใช้UTF8Encoding
คลาสของ. NET และส่งผ่าน$False
ไปยังตัวสร้างดูเหมือนจะทำงาน:
$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)
ก็พอ WriteAllLines
โอเวอร์โหลดนี้เขียน UTF8 ที่แน่นอนโดยไม่มี BOM
WriteAllLines
ดูเหมือนว่าจะต้อง$MyPath
มีแน่นอน
WriteAllLines
[System.Environment]::CurrentDirectory
หากคุณเปิด PowerShell แล้วเปลี่ยนไดเรกทอรีปัจจุบันของคุณ (โดยใช้cd
หรือSet-Location
) จากนั้น[System.Environment]::CurrentDirectory
จะไม่มีการเปลี่ยนแปลงและไฟล์จะอยู่ในไดเรกทอรีที่ไม่ถูกต้อง [System.Environment]::CurrentDirectory = (Get-Location).Path
คุณสามารถทำงานรอบนี้โดย
เหมาะสมวิธีที่เป็นอยู่ในตอนนี้คือการใช้วิธีการแก้ปัญหาที่แนะนำโดย @Roman Kuzmin ในความคิดเห็นเพื่อวิสัย ดัดลีย์ตอบ :
[IO.File]::WriteAllLines($filename, $content)
(ฉันยังย่อให้สั้นลงเล็กน้อยโดยการSystem
ล้างการชี้แจงเนมสเปซที่ไม่จำเป็น- มันจะถูกแทนที่โดยอัตโนมัติตามค่าเริ่มต้น)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
ฉันคิดว่าสิ่งนี้จะไม่เป็น UTF แต่ฉันเพิ่งค้นพบวิธีแก้ปัญหาง่ายๆที่ดูเหมือนว่าจะทำงาน ...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
สำหรับฉันผลลัพธ์นี้เป็น utf-8 โดยไม่มีไฟล์ bom โดยไม่คำนึงถึงรูปแบบของแหล่งที่มา
-encoding utf8
ตามความต้องการของฉัน
-Encoding ASCII
หลีกเลี่ยงปัญหา BOM แต่เห็นได้ชัดว่าคุณได้รับอักขระ ASCII 7 บิตเท่านั้น ระบุว่า ASCII เป็นส่วนหนึ่งของ UTF-8, แฟ้มผลเป็นเทคนิคที่ยัง UTF-8 ไฟล์ที่ถูกต้อง แต่ทุกอักขระที่ไม่ใช่ ASCII ในการป้อนข้อมูลของคุณจะถูกแปลงเป็นตัวอักษร?
ตัวอักษร
-encoding utf8
ยังส่งออก UTF-8 ด้วย BOM :(
หมายเหตุ: คำตอบนี้ใช้กับWindows PowerShell ; ในทางตรงกันข้ามใน PowerShell Core edition (v6 +) ข้ามแพลตฟอร์มUTF-8 ที่ไม่มี BOMคือการเข้ารหัสเริ่มต้นสำหรับทุก cmdlet
ในคำอื่น ๆ : หากคุณกำลังใช้PowerShell [หลัก] รุ่น 6 หรือสูงกว่าคุณจะได้รับ BOM น้อย UTF-8 ไฟล์โดยค่าเริ่มต้น (ซึ่งคุณสามารถขออย่างชัดเจนด้วย-Encoding utf8
/ -Encoding utf8NoBOM
ขณะที่คุณจะได้รับกับ -BOM เข้ารหัสด้วย-utf8BOM
)
เพื่อเติมเต็มคำตอบที่ง่ายและปฏิบัติของ M. Dudley (และการปฏิรูปที่กระชับยิ่งขึ้นของ ForNeVeR ):
เพื่อความสะดวกต่อไปนี้เป็นฟังก์ชั่นขั้นสูงOut-FileUtf8NoBom
, ทางเลือกที่ท่อตามที่เลียนแบบOut-File
ซึ่งหมายถึง:
Out-File
ในท่อOut-File
วัตถุการป้อนข้อมูลที่ไม่ได้รับสายที่มีรูปแบบที่พวกเขาจะเป็นอย่างไรถ้าคุณส่งพวกเขาไปยังคอนโซลเช่นเดียวกับตัวอย่าง:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
ให้สังเกตว่า(Get-Content $MyPath)
มีการปิดล้อมไว้ใน(...)
ที่ใดเพื่อให้แน่ใจว่าไฟล์ทั้งหมดถูกเปิดอ่านเต็มและปิดก่อนที่จะส่งผลลัพธ์ผ่านไปป์ไลน์ นี่เป็นสิ่งที่จำเป็นเพื่อให้สามารถเขียนกลับไปที่ไฟล์เดียวกัน (อัปเดตในที่ )
โดยทั่วไปแม้ว่าเทคนิคนี้ไม่แนะนำให้เลือกด้วยเหตุผล 2 ประการ: (a) ไฟล์ทั้งหมดจะต้องพอดีกับหน่วยความจำและ (b) หากคำสั่งถูกขัดจังหวะข้อมูลจะหายไป
หมายเหตุเกี่ยวกับการใช้หน่วยความจำ :
ซอร์สโค้ดของ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.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()
}
}
การเริ่มต้นจากรุ่น 6 PowerShell รองรับการUTF8NoBOM
เข้ารหัสทั้งสำหรับชุดเนื้อหาและไฟล์ออกและยังใช้สิ่งนี้เป็นการเข้ารหัสเริ่มต้น
ดังนั้นในตัวอย่างข้างต้นควรเป็นดังนี้:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
เมื่อใช้Set-Content
แทนคุณOut-File
สามารถระบุการเข้ารหัสByte
ซึ่งสามารถใช้ในการเขียนอาร์เรย์ไบต์ลงในไฟล์ เมื่อรวมกับการเข้ารหัส UTF8 แบบกำหนดเองซึ่งไม่ปล่อย BOM จะให้ผลลัพธ์ที่ต้องการ:
# 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()
หรือคล้ายกันคือควรทำงานได้ดีกับรายการและพา ธ ทุกประเภท
สคริปต์นี้จะแปลงเป็น UTF-8 โดยไม่มี BOM ไฟล์. txt ทั้งหมดใน DIRECTORY1 และส่งออกเป็น 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"
}
แหล่งที่มาวิธีลบ UTF8 Byte Order Mark (BOM) ออกจากไฟล์โดยใช้ PowerShell
หากคุณต้องการใช้[System.IO.File]::WriteAllLines()
คุณควรแปลงพารามิเตอร์ตัวที่สองเป็นString[]
(หากชนิดของ$MyFile
is 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 โดยไม่มี BOM
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
คือConvert-Path $MyPath
; หากคุณต้องการให้แน่ใจว่ามี CRLF ต่อท้ายให้ใช้[System.IO.File]::WriteAllLines()
คู่กับสตริงอินพุตเดียว (ไม่จำเป็นOut-String
)
เทคนิคหนึ่งที่ฉันใช้คือเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์ ASCII โดยใช้Out-File cmdlet
ตัวอย่างเช่นฉันมักจะเรียกใช้สคริปต์ 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
-Encoding ASCII
หลีกเลี่ยงปัญหา BOM แต่คุณเห็นได้ชัดเพียง แต่ได้รับการสนับสนุนสำหรับอักขระ ASCII 7 บิต ระบุว่า ASCII เป็นส่วนหนึ่งของ UTF-8, แฟ้มผลเป็นเทคนิคที่ยัง UTF-8 ไฟล์ที่ถูกต้อง แต่ทุกอักขระที่ไม่ใช่ ASCII ในการป้อนข้อมูลของคุณจะถูกแปลงเป็นตัวอักษร?
ตัวอักษร
เปลี่ยนไฟล์หลายไฟล์โดยขยายเป็น UTF-8 โดยไม่มี 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)
}
ไม่ว่าจะด้วยเหตุผลใดก็ตามการWriteAllLines
โทรยังคงสร้าง BOM สำหรับฉันด้วยUTF8Encoding
อาร์กิวเมนต์BOMless และไม่ได้ทำ แต่สิ่งต่อไปนี้ใช้ได้กับฉัน:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
ฉันต้องทำให้เส้นทางไฟล์สมบูรณ์เพื่อให้ใช้งานได้ มิฉะนั้นจะเขียนไฟล์ไปยังเดสก์ท็อปของฉัน นอกจากนี้ฉันคิดว่ามันใช้งานได้ก็ต่อเมื่อคุณรู้ว่า BOM ของคุณคือ 3 ไบต์ ฉันไม่รู้เลยว่ามันน่าเชื่อถือแค่ไหนที่จะคาดหวังรูปแบบ / ความยาว BOM ที่ได้รับจากการเข้ารหัส
นอกจากนี้ตามที่เขียนไว้อาจใช้งานได้เฉพาะในกรณีที่ไฟล์ของคุณอยู่ในอาร์เรย์ PowerShell ซึ่งดูเหมือนว่าจะมีขีดจำกัดความยาวของค่าต่ำกว่า[int32]::MaxValue
ในเครื่องของฉัน
WriteAllLines
โดยไม่ต้องมีการเข้ารหัสการเข้ารหัสไม่เคยเขียน BOM เองแต่เป็นไปได้ที่สตริงของคุณจะเริ่มต้นด้วยอักขระ BOM ( U+FEFF
) ซึ่งการเขียนได้สร้าง UTF-8 BOM อย่างมีประสิทธิภาพ เช่น: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(ละเว้นการ[char] 0xfeff +
เพื่อดูว่าไม่มีการเขียน BOM)
[Environment]::CurrentDirectory = $PWD.ProviderPath
หรือเป็นทางเลือกที่ทั่วไปมากขึ้นกับ"$(pwd)\..."
วิธีการของคุณ(ดีกว่า: "$pwd\..."
ดียิ่งขึ้น: "$($pwd.ProviderPath)\..."
หรือ(Join-Path $pwd.ProviderPath ...)
) ใช้(Convert-Path BOMthetorpedoes.txt)
U+FEFF
สามารถใช้ด้านล่างเพื่อรับ UTF8 โดยไม่มี BOM
$MyFile | Out-File -Encoding ASCII
ASCII
ไม่ได้เป็น UTF-8 แต่มันไม่ ALS เพจรหัส ANSI ปัจจุบัน - คุณกำลังความคิดของDefault
; ASCII
แท้จริงคือการเข้ารหัส ASCII 7 บิตโดย codepoints> = 128 การแปลงเป็น?
อินสแตนซ์ตัวอักษร
-Encoding ASCII
เป็นจริง 7 บิต ASCII เท่านั้น: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- The ได้รับการทับศัพท์ไปä
?
ในทางตรงกันข้าม-Encoding Default
("ANSI") จะเก็บรักษาไว้อย่างถูกต้อง
อันนี้ใช้ได้กับฉัน (ใช้ "Default" แทน "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
ผลลัพธ์คือ ASCII ที่ไม่มี BOM
Default
เข้ารหัสจะใช้หน้ารหัส ANSI ปัจจุบันของระบบซึ่งไม่ใช่ UTF-8 ตามที่ฉันต้องการ