Expand-Archive(もしくはExtractToDirectory)で文字化けする場合の対応方法〔PowerShell x 解凍〕
先日、ZIPファイルの解凍処理を行うPowerShellスクリプトを作成しました。そのスクリプトでテストした結果、なぜか解凍後のファイル名が文字化けしてしまう現象を確認。
調べた結果、解凍対象のZIPファイルがShift JISエンコードで圧縮されたファイルとなり、PowerShellの標準的なコマンドレットやメソッドを使うと文字コードの不一致により文字化けしていることがわかりました。
それらをふまえた対応方法を紹介します。
この記事のターゲット
- PowerShellユーザーの方
- 日本語などマルチバイトの名前を含むZIPファイルを正常に解凍したい方
なぜ、PowerShellの解凍で文字化けしてしまうのか
PowerShellで標準搭載されている機能は下記の2点です。
-
Expand-Archive: PowerShell標準搭載のコマンドレット
-
ExtractToDirectory: .NET(ドットネット)のフレームワークから呼び出すメソッド
$zipPath = "解凍するZIPファイルのパス"
$outputPath = "解凍後の出力先のパス"
# PowerShell標準搭載のコマンドレット
Expand-Archive -Path $zipPath -DestinationPath $outputPath
# .NETから呼び出すメソッド
Add-Type -AssemblyName System.IO.Compression.FileSystem
([System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $outputPath))
これら機能は、各バージョンのPowerShellに標準搭載。これらのエンコードは「UTF-8」が採用されています。
一方、Windows 10(Win11では未検証)におけるGUI操作の古い規格の圧縮ツール(例: 右クリック → 送る → ZIP圧縮)では、
OSの文字コードを参照し「Shift-JIS」が採用。
このように圧縮した際のエンコードと解凍する時のエンコードがチグハグに。
結果、Shift-JISで圧縮したデータをUTF-8で解凍してたため、マルチバイト(日本語)を含むフォルダー名やファイル名が文字化けしていた事がわかりました。
なお、発生した不具合は名前のみで 解凍処理そのものは正常終了 しています。
つぎは、実際にどのような流れで文字化けするか確認してみましょう。
実際に文字化けを確認してみる
まず、フォルダー内に日本語を含む名前のファイルを準備。同じ場所に エンコード Shift-JISで圧縮 したファイルを配置しておきます。
PS D:\Desktop\Test_7Zip4Powershell> tree /F .\CompressedFile-Containing-MultibyteCharacters
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\COMPRESSEDFILE-CONTAINING-MULTIBYTECHARACTERS
Only-Alphabet.txt
マルチバイト(日本語)を含む.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
下記のとおり、PowerShellコマンドレット「Expand-Archive」で解凍すると解凍そのものはエラーなく終えますが、
解凍先のファイル名が文字化けしてしまいました。
PS D:\Desktop\Test_7Zip4Powershell> $zipPath = ".\CompressedFile-Containing-MultibyteCharacters.zip"
PS D:\Desktop\Test_7Zip4Powershell> $outputPath = ".\"
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> Expand-Archive -Path $zipPath -DestinationPath $outputPath
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /F .\CompressedFile-Containing-MultibyteCharacters
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\COMPRESSEDFILE-CONTAINING-MULTIBYTECHARACTERS
Only-Alphabet.txt
�}���`�o�C�g�i���{��j���܂�.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>Powershell>
.NETにおける System.IO.Compression.ZipFile
のメソッド「ExtractToDirectory」で解凍しても、
Expand-Archive
と同様に解凍はエラーなく終えるが、解凍先は文字化けという結果に。
PS D:\Desktop\Test_7Zip4Powershell> [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $outputPath)
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /F .\CompressedFile-Containing-MultibyteCharacters
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\COMPRESSEDFILE-CONTAINING-MULTIBYTECHARACTERS
Only-Alphabet.txt
�}���`�o�C�g�i���{��j���܂�.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
補足情報: エンコードがShift-JISの圧縮処理をコードで行い、文字化けを検証してみました。
$CompressFrom = "D:\Desktop\Test_7Zip4Powershell\TargetFolder"
$CompressTo = "D:\Desktop\Test_7Zip4Powershell\TargetFolder.zip"
$ExtractFrom = "D:\Desktop\Test_7Zip4Powershell\TargetFolder.zip"
$ExtractTo = "D:\Desktop\Test_7Zip4Powershell\Extract"
ツリーコマンドで圧縮するデータをチェック。
PS D:\Desktop\Test_7Zip4Powershell> tree /f .\TargetFolder
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\TARGETFOLDER
Only-Alphabet.txt
マルチバイト(日本語)のみ.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
下記のコマンドによりエンコードをShift-JISで圧縮する。
# ZIPファイルを生成
New-Item -ItemType File -Path $CompressTo
# ZIPファイルにデータを格納
$shell = New-Object -ComObject Shell.Application
$zip = $shell.NameSpace($CompressTo)
$source = $shell.NameSpace($CompressFrom)
$source.Items() | ForEach-Object { $zip.CopyHere($_) }
つぎに下記2通りの解凍。PowerShell標準機能の解凍であるため、エンコードがUTF-8。
# エンコードUTF-8の解凍コマンド その1
Expand-Archive $ExtractFrom $ExtractTo
# エンコードUTF-8の解凍コマンド その2
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($ExtractFrom , $ExtractTo)
想定通り、どちらのコマンドでも文字化けが発生。
PS D:\Desktop\Test_7Zip4Powershell> tree /F .\Extract\
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\EXTRACT
Only-Alphabet.txt
�}���`�o�C�g�i���{��j�̂�.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
今度は圧縮のエンコードをShift-JISではなく、UTF-8でエンコードするPowerShellのコマンド・メソッドで対応してみる。
# エンコードUTF-8の圧縮コマンド その1
Compress-Archive $CompressFrom $CompressTo
# エンコードUTF-8の圧縮コマンド その2
Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::CreateFromDirectory($CompressFrom , $CompressTo)
前述した2通りの解凍コマンドを使うと、文字化けせずにすべての解凍処理が正常終了したことを確認。
# エンコードがUTF-8で圧縮・解凍その1
PS D:\Desktop\Test_7Zip4Powershell> Compress-Archive $CompressFrom $CompressTo
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> Expand-Archive $ExtractFrom $ExtractTo
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /f .\Extract
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\EXTRACT
└─TargetFolder
Only-Alphabet.txt
マルチバイト(日本語)のみ.txt
PS D:\Desktop\Test_7Zip4Powershell>
# エンコードがUTF-8で圧縮・解凍その2
PS D:\Desktop\Test_7Zip4Powershell> Add-Type -AssemblyName System.IO.Compression.FileSystem
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> [System.IO.Compression.ZipFile]::CreateFromDirectory($CompressFrom , $CompressTo)
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> [System.IO.Compression.ZipFile]::ExtractToDirectory($ExtractFrom , $ExtractTo)
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /f .\Extract
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\EXTRACT
Only-Alphabet.txt
マルチバイト(日本語)のみ.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
以上の結果から圧縮時のエンコードがUTF-8以外(今回はShift-JIS)のデータに対し、エンコードUTF-8の解凍処理を行ったことで文字化けしたことが、はっきりとわかりました。
解決方法(回避方法)
方法1. 圧縮方法をエンコードUTF-8に変える
まずシンプルで手っ取り早い解決方法は、「圧縮する際のエンコードもUTF-8にする」です。
PowerShellのコマンドレットだと Compress-Archive "引数1" "引数2"
。
.NETのフレームワークを使用する方法だと [System.IO.Compression.ZipFile]::CreateFromDirectory("引数1", "引数2")
。
コマンドに固執する必要がなければ、7-ZipなどGUIで操作可能なアーカイバーを使うといいでしょう。
ただ、外部プログラムや外部システムで連携されたZIPファイルをそのまま使用しなければいけないケースの場合だと異なる対応方法が必要です。
方法2. 解凍のエンコードを「Shift-JIS」で指定する(ExtractToDirectoryを使用)
要件としてエンコードShift-JISで圧縮されたZIPファイルを解凍する必要がある場合、GUI操作で問題なければ 7-Zip などの自動でエンコードを判定する機能があるアーカイバーを使う方法ですが、
バッチやスクリプトで対応しなければならない場合は、ExtractToDirectory
で対応可能です。
ExtractToDirectory
には、解凍対象と解凍先以外に3つ目の引数で解凍時のエンコードを指定できます。
$encodingShiftjis = [System.Text.Encoding]::GetEncoding("shift_jis")
[System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath, $encodingShiftjis)
# エンコードを指定しないとUTF-8で解凍するため、文字化けする
PS D:\Desktop\Test_7Zip4Powershell> [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath)
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /f .\Extract
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\EXTRACT
└─TargetFolder
Only-Alphabet.txt
�}���`�o�C�g�i���{��j�̂�.txt
PS D:\Desktop\Test_7Zip4P
# エンコードを「Shift-JIS」で指定すると文字化けしない
PS D:\Desktop\Test_7Zip4Powershell> $encoding = [System.Text.Encoding]::GetEncoding("shift_jis")
>> [System.IO.Compression.ZipFile]::ExtractToDirectory($zipPath, $extractPath, $encoding)
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /f .\Extract
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\EXTRACT
└─TargetFolder
Only-Alphabet.txt
マルチバイト(日本語)のみ.txt
PS D:\Desktop\Test_7Zip4Powershell>
ただ、複数の企業から外部連携されるなどの要因により圧縮時のエンコードを取り決められていないケースだと、
また違う対応方法の検討が必要です。
※ 脱線しますが、ここで例にあげている課題のあるべき姿は、外部要件で圧縮ファイルのエンコードを取り決めるべきです。
方法3. 自動で解凍時のエンコードを判定可能な「7Zip4Powershell」
GUI操作が問題なければ、7-Zipなどのアーカイバーで対応可能。
圧縮時のエンコードが不明な圧縮ファイルが解凍対象の場合は、PowerShellで標準搭載されているコマンドだと文字化けを回避できません。
そのようなケースを調べた結果、「7Zip4Powershell」というモジュールに行き着きました。
現在、精力的には更新されていないようですが、「7Zip4Powershell」はオープンソースのモジュールで一定の信頼性はありそう。
このモジュールを使用することで圧縮ファイルのエンコードを自動で判定し、文字化けしないように解凍してくれます。
「7Zip4Powershell」モジュールのインストール
下記のコマンドで「7Zip4Powershell」モジュールをインストールできます。初回はインストールして良いか確認メッセージが表示されるので、
問題なければ Y
か A
を選択してインストールを続行してください。
PS C:\Users\XXXX> Install-Module -Name 7Zip4Powershell
Untrusted repository
You are installing the modules from an untrusted repository. If you trust this repository, change its
InstallationPolicy value by running the Set-PSRepository cmdlet. Are you sure you want to install the modules from
'PSGallery'?
[Y] Yes [A] Yes to All [N] No [L] No to All [S] Suspend [?] Help (default is "N"): a
PS C:\Users\Administrator>
# 対象のモジュールがインストールされたこと①
PS C:\Users\Administrator> Get-InstalledModule
Version Name Repository Description
------- ---- ---------- -----------
2.7.0 7Zip4Powershell PSGallery Powershell module for creating and extra…
PS C:\Users\XXXX>
# 対象のモジュールがインストールされたこと②
PS C:\Users\XXXX> Get-Package -name 7Zip4Powershell
Name Version Source ProviderName
---- ------- ------ ------------
7Zip4Powershell 2.7.0 https://www.powershellgallery.c… PowerShellGet
PS C:\Users\XXXX>
「Expand-7Zip」で解凍すると文字化けしない
下記のとおり、引数でエンコードを指定しなくても自動的に判定し正常に解凍してくれます。
PS D:\Desktop\Test_7Zip4Powershell> Expand-7Zip -ArchiveFileName $zipPath -TargetPath $outputPath
PS D:\Desktop\Test_7Zip4Powershell>
PS D:\Desktop\Test_7Zip4Powershell> tree /F .\CompressedFile-Containing-MultibyteCharacters
フォルダー パスの一覧: ボリューム ボリューム
ボリューム シリアル番号は 0000XXXX XXXX:XXXX です
D:\DESKTOP\TEST_7ZIP4POWERSHELL\COMPRESSEDFILE-CONTAINING-MULTIBYTECHARACTERS
Only-Alphabet.txt
マルチバイト(日本語)を含む.txt
サブフォルダーは存在しません
PS D:\Desktop\Test_7Zip4Powershell>
まとめ
- 文字化けしてしまう原因は、圧縮時と解凍時のエンコードが不一致となり、文字化けしていた!
- シンプルな解決方法は、圧縮・解凍の両方ともエンコードがUTF-8のツール・コマンドを使用する方法
- 上記にあるUTF8の対応方法が難しい場合は、
ExtractToDirector
の引数で解凍する際のエンコードを指定することで回避・解決! - 対象ファイルのエンコードが不明な場合に関しては、圧縮データのエンコードを自動的に判定し解凍してくれるモジュール 7Zip4Powershell の「Expand-7Zip」を使用することで回避・解決できた!
強力な機能をもっているPowerShellですが、とくに言語まわりの細かい不具合が多い印象です。
おそらく「Shift-JISが関係のない英語OSを使用」したり、「フォルダー名やファイル名にマルチバイト(日本語)を使用しない」とすれば問題ないのでしょうが、ロケーションが日本だとその対応方法は難しそう。
いっそのこと、互換を捨てたグローバル用のWindows OSというのも需要がありそうな気がしますが、どうなんでしょうね。
参考文献
関連記事
Discussion
この記事の内容で1点補足があります。
調べた結果、ExtractToDirectoryの3つ目の引数でエンコードを指定できました。
検証も済んでいるので、後日こちらの記事に追記する予定です。
2025.04.21 追記しました。