竹蔵のだいあり - stakezo’s diary

健忘録として。役に立つかな?という情報も

7zip 個別圧縮方法 と 圧縮率、マルチスレッドの考察

健忘録。(7zipでは、7za や 7z を使ったコマンドラインの圧縮方がありますが、この記事では 7z.exe を利用したコマンドラインの方法を紹介しています。)

最初にまとめ

個別フォルダ圧縮
chcp 65001
setlocal ENABLEDELAYEDEXPANSION
set x=%*
FOR %%f IN (!x!) DO ( 7z a -mx=5 "%%~dpf%%~nf.7z" %%f\* )
ECHO 7z 個別圧縮を完了しました。
pause

このバッチファイルを作成し、複数フォルダを D&D(ドラッグ&ドロップ )すると処理が始まります。(例:c:\a\b.txt という構成の、フォルダa をD&Dすると、a\b.txt という構成のa.7z が作成されます ※a\a\b.txt のようにはなりません。)

個別ファイル圧縮

※2022/08/01追記:

chcp 65001
setlocal ENABLEDELAYEDEXPANSION
set x=%*
FOR %%f IN (!x!) DO ( 7z a -mx=5 "%%~dpf%%~nf.7z" %%f )
endlocal
ECHO 7z 個別圧縮を完了しました。
pause

個別にファイルを圧縮する場合は以下のコマンドです。作成したバッチファイルに複数ファイルをD&Dします。
 






複数のフォルダを個別に圧縮したい場合があります。(WinRARの「ファイルごとに別の書庫にする」機能みたいなものです。)コマンドプロンプトを用いた個別圧縮を思いつきますが、良い記事を見つけました。(※2022/08/01追記:複数のファイルを個別に圧縮する場合も、記事の一番最後に追加しました)
 
参考:https://aprico-media.com/posts/3687 ありがとうございます!

@ECHO OFF

SET path="C:\Program Files\7-Zip\"
SET exeFile=7z.exe

REM zip解凍パスワード(%はエスケープのため%%とする)
SET pass="XXXXXXXX"

FOR %%f IN (%*) DO (
  %path%%exeFile% a -p%pass% %%~dpf%%~nf.zip %%f
)

ECHO zip圧縮完了しました。

pause

今回の目的に合うよう改良します。

↓改良後

chcp 65001
setlocal ENABLEDELAYEDEXPANSION
set x=%*
FOR %%f IN (!x!) DO ( 7z a -mx=5 "%%~dpf%%~nf.7z" %%f\* )
ECHO 7z 個別圧縮を完了しました。
pause

このバッチファイルを作成し、複数フォルダを ドラッグ&ドロップ すると処理が始まります。

以下、改良に至った考察です。
  
いくつかのポイントを改良します。
 

  • エコー非表示の削除

エコーは、あってもなくても動作に違いは無いのですが、OFFにしても、コマンド部分だけ非表示になり、7zが吐き出すテキストはいずれにせよ出力されます。なので今回は表示させるため削除します。

@ECHO OFF
↑削除
  • 7zまでのアドレスは環境変数があるため取り除く *1
SET path="C:\Program Files\7-Zip\"
SET exeFile=7z.exe

REM zip解凍パスワード(%はエスケープのため%%とする)
SET pass="XXXXXXXX"

FOR %%f IN (%*) DO (
  %path%%exeFile% a -p%pass% %%~dpf%%~nf.zip %%f
)
-----------------------------------------------------------------
 ↓変更後
-----------------------------------------------------------------
REM zip解凍パスワード(%はエスケープのため%%とする)
SET pass="XXXXXXXX"

FOR %%f IN (%*) DO (
  7z a -p%pass% %%~dpf%%~nf.zip %%f
)
  • パスワードはかけないため取り除く
REM zip解凍パスワード(%はエスケープのため%%とする)
SET pass="XXXXXXXX"

FOR %%f IN (%*) DO (
  7z a -p%pass% %%~dpf%%~nf.zip %%f
)
-----------------------------------------------------------------
 ↓変更後
-----------------------------------------------------------------
FOR %%f IN (%*) DO (
  7z a %%~dpf%%~nf.zip %%f
)

 

  • zip圧縮ではなく、7z圧縮を行う
  7z a %%~dpf%%~nf.zip %%f
  ↓変更後
  7z a %%~dpf%%~nf.7z %%f
  • 圧縮位置を、フォルダそのものではなく、フォルダ内にする*2
  7z a %%~dpf%%~nf.7z %%f
  ↓変更後
  7z a %%~dpf%%~nf.7z %%f\*

(バッチファイルの文字コードUTF-8にする場合、先頭に chcp 65001 を入れ、コマンドプロンプトの設定文字コードを Shift JIS から変更する必要がある。)

 chcp 65001

・ファイル名・フォルダ名に 「 」半角スペースが含まれていた場合、ファイル名の分裂が起こるため、ダブルクォーテーションで囲む

  7z a %%~dpf%%~nf.7z %%f\*
 ↓変更後
  7z a "%%~dpf%%~nf.7z" %%f\*
  • 圧縮率の変更。変数の最大値は9、最小は0 の10段階で、未記入の場合は -mx=5 と容量が一致した。 *3

 注釈 *3 で検証したように、バランスが取れているのが -mx=5 であるため、記入しなくても良いが、状況により、圧縮率を変えたいため *3、追加する

-mx=5

 

・圧縮後、元ファイル(フォルダ)の削除

-sdel
  • マルチスレッド化は以下のようなコマンドで指定できるが、未記入でも自動でONになるため、書く必要はない *4

 注釈 *4 で検証したように、マルチスレッドの方が大幅に時間を短縮できる。

-mmt=on

 
 

まとめ

出来上がったコマンドがこちら

chcp 65001
FOR %%f IN (%*) DO (
  7z a "%%~dpf%%~nf.7z" %%f\*
)
ECHO 7z 個別圧縮を完了しました。
pause

使い方は、↑の バッチファイル を作成。そのバッチファイルへ対象の フォルダ を ドラッグ&ドロップ すると実行されます。
※FOR文は1行にまとめても良い。

FOR %%f IN (%*) DO ( 7z a "%%~dpf%%~nf.7z" %%f )

 
 
 
 
 

更に細かい考察

*1  記事に戻る
注意点は、7zの環境変数を作っておくこと。「7z 環境変数」でググれば出てくる。例えばこちら
itojisan.xyz
 
*2  記事に戻る
前提として今回はフォルダの個別圧縮にスポットを当てているため、ファイルの圧縮は対象ではありません。
対策前と対策後の結果では以下のような違いがあります。

 C:\a\b.txt
 C:\a\c.txt
というフォルダ a と ファイル b.txt c.txt があったとします。
フォルダをドラッグ&ドロップし、C:\a.7z を作成。その後解凍。
対策前であれば
 C:\a\a\b.txt
 C:\a\a\c.txt
となり、対策後であれば
 C:\a\b.txt
 C:\a\c.txt
となります。
 
 
*3  記事に戻る
圧縮率と圧縮時間について
 【パターン1】 511,509KBの ISO ファイルを圧縮
    (ISOの圧縮はそもそも期待できない「圧縮しづらいもの」をあえて検証しています)
  未記入 = 22秒 475,325KB
  -mx=0 =  5秒 511,509KB
  -mx=4 = 22秒 483,559KB
  -mx=5 = 22秒 475,325KB <--- 未記入と一致
  -mx=6 = 24秒 475,008KB
  -mx=9 = 61秒 473,009KB
 
 【パターン2】 4KB x 5000個のフォーマットが決まったテキストファイル(とあるログファイル)
   合計サイズ 16.6MB (17,451,664 Byte)
  -mx=0 = 0.918秒 17,492,854 Byte
  -mx=4 = 0.951秒   60,378 Byte 圧縮率0.345972739%
  -mx=5 = 1.241秒   57,285 Byte 圧縮率0.328249501%
  -mx=6 = 1.234秒   57,285 Byte 圧縮率0.328249501%
  -mx=9 = 1.518秒   57,284 Byte 圧縮率0.328243770%
  ※処理時間は概算、100回ループさせた時間をストップウォッチで測って、100で割ったもの。
   圧縮率0.3% というのは 100% の容量が 0.3% まで縮んだという意味です
 
 以上の結果から、バランスが取れているのは 未記入 の -mx=5 だと思います。
 
 圧縮率を変える状況というのは、もともと圧縮率の高いファイルを圧縮する場合で-mx=0 つまり、無圧縮を行いたい状況です。例えば大量のjpg、mp3、mp4 など。この場合、-mx=0で圧縮をかけるということは、「ただまとめるだけ」という行為に等しいです。500KB x 10000枚 = 5GB の jpg 画像を個別にコピーする場合、数分かかる状況でも、5GB の 7zip 1ファイルであれば数秒で終わる可能性があります。つまり、「読み込み・書き込み速度の高速化」ですね。さらに、大量の jpg フォルダを圧縮する場合、もともと高圧縮という特徴を持つ jpg に更に圧縮をかけても高い期待は得られない。むしろ、圧縮後の方がファイルサイズが肥大する可能性すらあります。そのような場合は、 -mx=5 --> -mx=0 の無圧縮を選択したいと考えます。もし、それでも高い容量削減が必要ならば、 jpgのファイル側で 圧縮率を変えたり、webpへ変更など、別の角度から削減を検討したほうが良いと思います。これはmp3 や mp4 にも同じことがいえます。
「ただまとめるだけ」について・・・細かい話なのですが、ただまとめる といっても、まとまったファイルにはトップやボトム、zip であればさらにファイルの結合部に制御文字が入るので注意してください。単純にテキスト系ファイルを非可逆に結合したい場合は copy コマンドで結合するのがおすすめです。
例えば以下のバッチをダブルクリックで起動すると、同階層のテキストをWindowsの名前順に連結します。ただし、連結後はもとに戻すことができず、非可逆です。

copy /b *.txt matome.txt
pause

 
*4  記事に戻る
マルチスレッド化の違い
試しに500MBのファイルを圧縮

7z_cmd_マルチスレッドOFF_と_ON

 左が-mmt=OFF で 2分43秒
 右が未記入または -mmt=ON で 22秒
と、処理速度に大きな違いがありました。
マルチスレッドONの右のグラフは、各論理プロセッサが100%になっている期間があります。それが圧縮中の22秒間です。対して、左のマルチスレッドOFFでは目立ってCPUの使用率が上がっていませんでした。(予想では一つだけ上がると思ったけど、目立たなくてわからないw)


追記

※2022/08/01追記:個別にファイルを圧縮する場合は以下のコマンドです。作成したバッチファイルに複数ファイルをD&Dします。

chcp 65001
setlocal ENABLEDELAYEDEXPANSION
set x=%*
FOR %%f IN (!x!) DO ( 7z a -mx=5 "%%~dpf%%~nf.7z" %%f\* )
endlocal
ECHO 7z 個別圧縮を完了しました。
pause