Utiliser PowerShell pour écrire un fichier en UTF-8 sans le BOM

Out-File semble forcer le MOB lors de l'utilisation de l'UTF-8:

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

Comment puis-je écrire un fichier en UTF-8 sans BOM en utilisant PowerShell?

185
demandé sur M. Dudley 2011-04-08 19:02:39

14 réponses

à l'Aide .NET UTF8Encoding de la classe et en passant $False pour le constructeur semble fonctionner:

$MyFile = Get-Content $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)
174
répondu M. Dudley 2016-12-15 23:56:32

le correct la solution recommandée par @Roman Kuzmin dans les commentaires à @M. Dudley réponse :

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

(Je l'ai également raccourci un peu en enlevant inutile System clarification de l'espace de nom - il sera remplacé automatiquement par défaut.)

59
répondu ForNeVeR 2017-05-23 12:34:44

j'ai pensé que ce ne serait pas UTF, mais j'ai juste trouvé une solution assez simple qui semble fonctionner...

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

pour moi il en résulte un utf-8 Sans fichier bom quel que soit le format source.

32
répondu Lenny 2016-12-02 00:26:54

À compléter", 1519100920" M. Dudley est propre, simple et pragmatique réponse (et ForNeVeR est plus concis reformulation ):

pour plus de commodité, voici la fonction avancée Out-FileUtf8NoBom , une alternative basée sur la canalisation qui imite Out-File , qui signifie:

  • vous pouvez l'utiliser comme Out-File dans un pipeline.
  • objets d'entrée qui les chaînes ne sont pas formatées comme elles le seraient si vous les envoyiez à la console, comme avec Out-File .

exemple:

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

notez que (Get-Content $MyPath) est inclus dans (...) , ce qui garantit que le dossier entier est ouvert, lu en entier, et fermé avant d'envoyer le résultat à travers le pipeline. Ceci est nécessaire pour pouvoir écrire à nouveau dans le fichier même (mettez-le à jour) en place ).

En général, cependant, cette technique n'est pas recommandée pour deux raisons: (a) le fichier entier doit s'insérer dans la mémoire et (b) si la commande est interrompue, les données seront perdues.

Une note sur l'utilisation de la mémoire :

  • la réponse de M. Dudley exige que tout le contenu du fichier soit d'abord accumulé en mémoire, ce qui peut être problématique avec de gros fichiers.
  • la fonction ci-dessous n'améliore que légèrement ce point: tous les objets d'entrée sont encore tamponnés en premier, mais leurs représentations de chaîne sont alors générées et écrites dans le fichier de sortie une à une.

le code Source de Out-FileUtf8NoBom (également disponible comme le MIT sous licence de l'Essentiel ):

<#
.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
répondu mklement0 2017-10-24 22:26:37

ce script convertira, en UTF-8 sans BOM, tout .les fichiers TXT dans DIRECTORY1 et leur sortie dans DIRECTORY2

foreach ($i in ls -name DIRECTORY1\*.txt)
{
    $file_content = Get-Content "DIRECTORY1$i";
    [System.IO.File]::WriteAllLines("DIRECTORY2$i", $file_content);
}
4
répondu jamhan 2013-05-01 05:22:46

en utilisant Set-Content au lieu de Out-File , vous pouvez spécifier l'encodage Byte , qui peut être utilisé pour écrire un tableau d'octets à un fichier. Ceci en combinaison avec un encodage UTF8 personnalisé qui n'émet pas le BOM donne le résultat désiré:

# 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

la différence par rapport à l'utilisation de [IO.File]::WriteAllLines() ou similaire est qu'il devrait fonctionner très bien avec n'importe quel type d'article et le chemin, pas seulement les chemins de fichier réels.

3
répondu Lucero 2018-04-23 17:48:31

Avait le même problème. Cela a fait l'affaire pour moi:

$MyFile | Out-File -Encoding Oem $MyPath

lors de l'ouverture du fichier avec Visual Studio Code ou Notepad++ il s'affiche comme UTF-8

2
répondu delianmc 2018-07-19 18:51:53

une technique que j'utilise est de rediriger la sortie vers un fichier ASCII en utilisant le Out-File cmdlet.

par exemple, j'exécute souvent des scripts SQL qui créent un autre script SQL à exécuter dans Oracle. Avec une redirection simple ( " > " ), la sortie sera en UTF-16 qui n'est pas reconnu par SQLPlus. Pour contourner cela:

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

le script généré peut alors être exécuté via une autre session SQLPlus Sans Unicode inquiétudes:

sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
1
répondu Erik Anderson 2016-09-22 19:36:20

Modifier plusieurs fichiers par extension de l'UTF-8 sans 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)
}
0
répondu Jaume Suñer Mut 2016-10-03 13:59:08

pour quelque raison que ce soit, les appels WriteAllLines produisaient encore un BOM pour moi, avec l'argument BOMless UTF8Encoding et sans lui. Mais ce qui suit a fonctionné pour moi:

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

j'ai dû prendre le chemin de fichier absolu pour qu'il fonctionne. Sinon, il a écrit le fichier sur mon Bureau. En outre, je suppose que cela ne fonctionne que si vous savez que votre BOM est de 3 octets. Je n'ai aucune idée de la fiabilité à attendre d'un format/longueur BOM donné basé sur l'encodage.

aussi, tel qu'écrit, cela ne fonctionne probablement que si votre fichier s'inscrit dans un tableau powershell, qui semble avoir une limite de longueur d'une valeur inférieure à [int32]::MaxValue sur ma machine.

0
répondu xdhmoore 2017-02-01 06:28:43
    [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" 
    }  

Source comment faire pour supprimer UTF8 Byte Order Mark (BOM) à partir d'un fichier en utilisant PowerShell

0
répondu frank tan 2017-02-08 05:47:40

si vous voulez utiliser [System.IO.File]::WriteAllLines() , vous devez lancer deuxième paramètre à String[] (si le type de $MyFile est Object[] ), et aussi spécifier chemin absolu avec $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath) , comme:

$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)

si vous voulez utiliser [System.IO.File]::WriteAllText() , parfois vous devez pipe le deuxième paramètre dans | Out-String | pour ajouter crlfs à la fin de chaque ligne explicitement (surtout quand vous les utilisez avec 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)

ou vous peut utiliser [Text.Encoding]::UTF8.GetBytes() avec 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"

voir: Comment écrire la suite de ConvertTo-Csv dans un fichier en UTF-8 sans BOM

0
répondu satob 2018-04-24 13:42:19

pourrait utiliser ci-dessous pour obtenir UTF8 sans BOM

$MyFile | Out-File -Encoding ASCII
-2
répondu Robin Wang 2015-09-22 20:43:38

celui-ci fonctionne pour moi (utilisez "Default" au lieu de "UTF8"):

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

le résultat est ASCII sans BOM.

-3
répondu Krzysztof 2015-05-06 12:34:44