Out-File
wydaje się wymuszać BOM podczas korzystania z UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Jak mogę napisać plik w UTF-8 bez BOM za pomocą PowerShell?
encoding
powershell
utf-8
byte-order-mark
M. Dudley
źródło
źródło
Odpowiedzi:
Wydaje się, że używanie
UTF8Encoding
klasy .NET i przekazywanie$False
do konstruktora działa:źródło
[System.IO.File]::WriteAllLines($MyPath, $MyFile)
wystarczy. ToWriteAllLines
przeciążenie zapisuje dokładnie UTF8 bez BOM.WriteAllLines
że wymaga$MyPath
to absolutności.WriteAllLines
pobiera bieżący katalog z[System.Environment]::CurrentDirectory
. Jeśli otworzysz PowerShell, a następnie zmienisz swój bieżący katalog (używająccd
lubSet-Location
),[System.Environment]::CurrentDirectory
nie zostanie on zmieniony, a plik znajdzie się w niewłaściwym katalogu. Możesz obejść ten problem przez[System.Environment]::CurrentDirectory = (Get-Location).Path
.Właściwa droga jak na razie jest zastosowanie rozwiązania zalecanego przez @Roman Kuzmin w komentarzach do @M. Odpowiedź Dudleya :
(Trochę go też skróciłem, usuwając niepotrzebne
System
wyjaśnienie przestrzeni nazw - domyślnie zostanie zastąpione automatycznie).źródło
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Pomyślałem, że to nie będzie UTF, ale właśnie znalazłem dość proste rozwiązanie, które wydaje się działać ...
Dla mnie daje to utf-8 bez pliku BOM niezależnie od formatu źródłowego.
źródło
-encoding utf8
moich wymagań.-Encoding ASCII
unika się problemu BOM, ale oczywiście otrzymujesz tylko 7-bitowe znaki ASCII . Biorąc pod uwagę, że ASCII jest podzbiorem UTF-8, plik wynikowy jest technicznie również poprawnym plikiem UTF-8, ale wszystkie znaki inne niż ASCII w twoim danych wejściowych zostaną przekonwertowane na?
znaki dosłowne .-encoding utf8
nadal wysyła UTF-8 z BOM. :(Uwaga: ta odpowiedź dotyczy programu Windows PowerShell ; dla kontrastu w wieloplatformowej edycji PowerShell Core (v6 +) UTF-8 bez BOM jest domyślnym kodowaniem dla wszystkich poleceń cmdlet.
Innymi słowy: Jeśli używasz programu PowerShell [Core] w wersji 6 lub nowszej , domyślnie otrzymujesz pliki UTF-8 bez BOM (które możesz również jawnie zażądać za pomocą
-Encoding utf8
/-Encoding utf8NoBOM
, podczas gdy za pomocą kodowania -BOM za pomocą-utf8BOM
).Aby uzupełnić własną prostą i pragmatyczną odpowiedź M. Dudleya (i bardziej zwięzłą przeformułowanie ForNeVeR ):
Dla wygody, oto zaawansowana funkcja
Out-FileUtf8NoBom
, oparta na potoku alternatywa, która naśladujeOut-File
, co oznacza:Out-File
w potoku.Out-File
.Przykład:
Zwróć uwagę na sposób, w jaki
(Get-Content $MyPath)
jest zamknięty(...)
, co gwarantuje, że cały plik zostanie otwarty, w pełni odczytany i zamknięty przed wysłaniem wyniku przez potok. Jest to konieczne, aby móc ponownie zapisać w tym samym pliku (zaktualizować go w miejscu ).Ogólnie jednak technika ta nie jest wskazana z dwóch powodów: (a) cały plik musi zmieścić się w pamięci i (b) jeśli polecenie zostanie przerwane, dane zostaną utracone.
Uwaga na temat wykorzystania pamięci :
Kod źródłowy
Out-FileUtf8NoBom
(dostępny również jako Gist na licencji MIT ):źródło
Począwszy od wersji 6 program PowerShell obsługuje
UTF8NoBOM
kodowanie zarówno dla zawartości zestawu, jak i pliku wyjściowego, a nawet używa go jako kodowania domyślnego.W powyższym przykładzie powinno to wyglądać tak:
źródło
$PSVersionTable.PSVersion
Używając
Set-Content
zamiastOut-File
, możesz określić kodowanieByte
, którego można użyć do zapisania tablicy bajtów w pliku. To w połączeniu z niestandardowym kodowaniem UTF8, które nie emituje BOM, daje pożądany rezultat:Różnica w stosowaniu
[IO.File]::WriteAllLines()
lub podobnym polega na tym, że powinien on działać poprawnie z każdym typem elementu i ścieżki, nie tylko rzeczywistymi ścieżkami plików.źródło
Ten skrypt konwertuje, do UTF-8 bez BOM, wszystkie pliki .txt w DIRECTORY1 i wysyła je do DIRECTORY2
źródło
Źródło Jak usunąć UTF8 Byte Order Mark (BOM) z pliku za pomocą PowerShell
źródło
Jeśli chcesz użyć
[System.IO.File]::WriteAllLines()
, powinieneś rzucić drugi parametr naString[]
(jeśli typ$MyFile
jestObject[]
), a także określić ścieżkę bezwzględną za pomocą$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
:Jeśli chcesz użyć
[System.IO.File]::WriteAllText()
, czasami powinieneś| Out-String |
przesłać drugi parametr, aby dodać CRLF na końcu każdej linii w sposób wyraźny (szczególnie, gdy używasz ich zConvertTo-Csv
):Lub możesz użyć
[Text.Encoding]::UTF8.GetBytes()
zSet-Content -Encoding Byte
:patrz: Jak zapisać wynik ConvertTo-Csv do pliku w UTF-8 bez BOM
źródło
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
jestConvert-Path $MyPath
; jeśli chcesz zapewnić końcowe CRLF, po prostu użyj[System.IO.File]::WriteAllLines()
nawet z jednym ciągiem wejściowym (nie ma takiej potrzebyOut-String
).Jedną z technik, których używam, jest przekierowanie danych wyjściowych do pliku ASCII za pomocą polecenia cmdlet Out-File .
Na przykład często uruchamiam skrypty SQL, które tworzą inny skrypt SQL do wykonania w Oracle. Dzięki prostemu przekierowaniu („>”) dane wyjściowe będą w formacie UTF-16, który nie jest rozpoznawany przez SQLPlus. Aby obejść ten problem:
Wygenerowany skrypt może być następnie wykonany przez inną sesję SQLPlus bez żadnych obaw związanych z Unicode:
źródło
-Encoding ASCII
pozwala uniknąć problemu BOM, ale oczywiście otrzymujesz wsparcie tylko dla 7-bitowych znaków ASCII . Biorąc pod uwagę, że ASCII jest podzbiorem UTF-8, plik wynikowy jest technicznie również poprawnym plikiem UTF-8, ale wszystkie znaki inne niż ASCII w twoim danych wejściowych zostaną przekonwertowane na?
znaki dosłowne .Zmień wiele plików przez rozszerzenie na UTF-8 bez BOM:
źródło
Z jakiegokolwiek powodu
WriteAllLines
wywołania nadal generowały dla mnie BOM, zUTF8Encoding
argumentem Bez BOM i bez niego. Ale następujące działały dla mnie:Musiałem ustawić bezwzględną ścieżkę pliku, aby działała. W przeciwnym razie plik zostanie zapisany na moim pulpicie. Przypuszczam, że to działa tylko wtedy, gdy wiesz, że twój BOM ma 3 bajty. Nie mam pojęcia, jak wiarygodne jest oczekiwanie określonego formatu / długości BOM na podstawie kodowania.
Ponadto, jak napisano, prawdopodobnie działa to tylko wtedy, gdy plik mieści się w tablicy PowerShell, która wydaje się mieć limit długości o wartości niższej niż
[int32]::MaxValue
na moim komputerze.źródło
WriteAllLines
bez argumentu kodującego nigdy nie zapisuje samej BOM , ale możliwe jest, że Twój łańcuch zaczynał się od znaku BOM (U+FEFF
), który po napisaniu skutecznie stworzył BOM UTF-8; np .:$s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(pomiń,[char] 0xfeff +
aby zobaczyć, że BOM nie jest zapisany).[Environment]::CurrentDirectory = $PWD.ProviderPath
, albo jako bardziej ogólną alternatywę dla swojego"$(pwd)\..."
podejścia (lepiej"$pwd\..."
"$($pwd.ProviderPath)\..."
(Join-Path $pwd.ProviderPath ...)
(Convert-Path BOMthetorpedoes.txt)
U+FEFF
.Można użyć poniżej, aby uzyskać UTF8 bez BOM
źródło
ASCII
nie jest UTF-8, ale nie jest to również bieżąca strona kodowa ANSI - myślisz o tymDefault
;ASCII
naprawdę jest 7-bitowym kodowaniem ASCII, przy czym punkty kodowe> = 128 są konwertowane na dosłowne?
instancje.-Encoding ASCII
rzeczywiście jest to tylko 7-bitowy ASCII:'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
-ä
został transliterowany na a?
. Natomiast-Encoding Default
(„ANSI”) poprawnie to zachowałby.Ten działa dla mnie (użyj „Domyślne” zamiast „UTF8”):
Wynikiem jest ASCII bez BOM.
źródło
Default
kodowanie będzie używać bieżącej strony kodowej ANSI systemu, która nie jest UTF-8, jak wymagałem.