RAR:PECLなしでファイルのリストを取得する

少し前に、 DOCであろうとPDFであろうと、あらゆる種類のファイル形式からテキストを取得することについて書きました。 今日は、同様に興味深い形式であるRAR圧縮形式を検討します。 苦しんでいる人を安心させるつもりはありません-今日は、追加のPHP拡張機能なしでファイルのリストのみを読み取ります。 それで、それが面白い人に、私はキャットの下で尋ねます...



RARは良い「悪い」アーカイバです



RARは同胞のEugene Roshalによって開発されていることを思い出させてください。 彼から、彼はRoshal Archiverという名前を受け取りました。 この形式は閉鎖されており、ロシアと世界中の両方での配布にはまったく影響しませんでした。 私が見なければならなかったほとんどすべてのワークステーションは、RARアーカイバがインストールされていて、時々クラックされていました 。



その開発とアーカイバーである間、それはすぐに3番目のバージョン( 4番目になると思います)に成長しました。これはほとんどの「自作」アーカイバーに影響を与えました。 それにもかかわらず、開発者のサイトには、さまざまな開発プラットフォームおよび環境のRARファイルを解凍するための十分な量のあらゆる種類のソースコードが含まれています。



PHPに関しては、 PECL拡張モジュールは「安定した」最初のバージョンに成長しており、ホスティングサイトにインストールされることはほとんどありません。 ちなみに、この拡張機能は、ソースコードがプログラムのWebサイトにある「unrar」を使用します。 さらに、正直に言って、5.3(Windows)で拡張機能を動作させることはできませんでした。php_rar.dllでは5.2.11で動作しましたが、ほとんどのアーカイブを読み取ることができませんでした。 Windowsシステム用にコンパイルされたライブラリのすべてのバージョンが「ある種の」異なるバージョン用であることは驚くことではありませんが、私は自分でコンパイルしたくはありませんでした...サイトのソースコード。



RAR-どうですか?



データ圧縮のためのソースコードが存在するという事実にもかかわらず、形式の機密性のため、その形式に関するドキュメントはほとんどありません。 当然のことながら、約600 kbのソースコードを検討したい人はほとんどいません。 それにもかかわらず、愛好家はまだいます(私の人のことを考えているなら神は禁じています:)-したがって、 UniquE RAR File Libraryプロジェクトは一度に作成されました。これにより、アーカイバーの2番目のバージョンで作成されたファイルを解凍するためのソースコードが削減されました。



そのため、前述のライブラリのソース、およびアーカイバの古い2.02バージョンに関する最小限の(少なくとも一部の) ドキュメントに出会いました。 それでは、RARアーカイブがどのように見えるかを見てみましょう。



RARアーカイブは、それぞれ7バイトのヘッダーを持つ可変長ブロックで構成されています。 アーカイブには、少なくとも2つのMARK_HEADブロックとMAIN_HEADブロックが含まれます。 最初の行にはRARがあるという情報が含まれており、HEXでは「 52 61 72 21 1a 07 00



」のように見えます。 3番目のバイト072



は、これがマーカーヘッダーであることを示しています。 リトルエンディアンのワード00 07



には、ブロックの長さが含まれています。 ちょうど同じ7バイト。



2番目のMain Headerブロックは最初のブロックの直後に始まり、13バイトを含み、マーキングバイトが0x73



等しい必要があります。 その後、ファイルは既にデータを開始しています-圧縮ファイル(ブロックヘッダーの3バイト目の074



市場)、アーカイブに関するコメント、追加情報、または回復記録などです。



ファイルのリストを取得するアルゴリズムは複雑ではありません(暗号化されたディレクトリ構造のアーカイブを考慮しない場合、その読み取りはこの記事の範囲外です)。



  1. ヘッダーの最初の7バイトを読み取ります。 そこで見出しの長さを見つけて、最後まで読みます。
  2. ブロックが「ファイル」であるかどうかを確認してください。
  3. 「はい」の場合、7番目の位置のDWORDはアーカイブファイルのサイズ(および次のブロックの前に読み取る必要があるデータの量)、次のダブルワードはソースファイルのサイズ、28の位置にはファイル属性(DWORD)があり、アドレス26と32は、ファイル名の長さ(2バイト)と名前そのものです。 さらに、作成日、ファイルが作成されたOSコード、CRCを確認できます。
  4. ブロックが「ファイル」ではない場合、3番目の位置の単語を読み取り、その15番目のビットの値を確認する必要があります。これは、ブロックに関連する追加の情報量の原因となります。 この位置で「1」の場合、ADD_SIZEバイト(ブロックヘッダーの後の最初のダブルワード)をスキップする必要があります。
  5. そして、ファイルの最後まで...




難しいですか? 実際には、.docファイルと比較して。



ソースコード



  1. //使用せずに$ filenameからファイルのリストを読み取る関数
  2. // PECL拡張機能rar。
  3. 関数 rar_getFileList ( $ filename ) {
  4. //文字列からCOUNTバイトを取得する関数(リトルエンディアン)。
  5. //関数のグローバル空間を散らかさないために-送信する
  6. //母体を反転します。
  7. if ( ! function_exists ( "temp_getBytes" ) )) {
  8. function temp_getBytes ( $ data 、 $ from 、 $ count ) {
  9. $ string = substr ( $ data 、 $ from 、 $ count ) ;
  10. $ string = strrev ( $ string ) ;
  11. return hexdec ( bin2hex ( $ string ) ) ;
  12. }
  13. }
  14. //ファイルを開こうとします
  15. $ id = fopen ( $ filename 、 "rb" ) ;
  16. if ( ! $ id )
  17. falseを 返し ます 。
  18. //ファイルがRARアーカイブかどうかを確認します
  19. $ markHead = fread ( $ id 、 7 ) ;
  20. if ( bin2hex ( $ markHead ) != "526172211a0700" )
  21. falseを 返し ます 。
  22. // MAIN_HEADブロックを読み取ろうとしています
  23. $ mainHead = fread ( $ id 、 7 ) ;
  24. if ( ord ( $ mainHead [ 2 ] ) != 0x73 )
  25. falseを 返し ます 。
  26. $ headSize = temp_getBytes ( $ mainHead 、 5、2 ) ;
  27. //ファイル内の最初の「重要な」ブロックの位置に移動します
  28. fseek ( $ id 、 $ headSize - 7 、 SEEK_CUR ) ;
  29. $ files = array ( ) ;
  30. while ( ! feof ( $ id ) ) {
  31. //ブロックヘッダーを読み取ります
  32. $ block = fread ( $ id 、 7 ) ;
  33. $ headSize = temp_getBytes ( $ block 、 5、2 ) ;
  34. if ( $ headSize <= 7 )
  35. 休憩 ;
  36. //ヘッダーの長さに基づいてブロックの残りを読み取ります
  37. //対応するオフセット
  38. $ブロック = fread ( $ id 、 $ headSize - 7 ) ;
  39. //これがファイルブロックの場合、処理を開始します
  40. if ( ord ( $ block [ 2 ] ) == 0x74 ) {
  41. //圧縮されたファイルがアーカイブでどれくらいかかるかを調べ、
  42. //次の位置に移動します。
  43. $ packSize = temp_getBytes ( $ブロック 、 7、4 ) ;
  44. fseek ( $ id 、 $ packSize 、 SEEK_CUR ) ;
  45. //ファイル属性を読み取ります:r-読み取り専用、h-非表示、
  46. // s-システム、d-ディレクトリ、a-アーカイブ済み
  47. $ attr = temp_getBytes ( $ block 、 28、4 ) ;
  48. $属性 = "" ;
  49. if ( $ attr & 0x01 )
  50. $ attributes 。= "r" ;
  51. if ( $ attr & 0x02 )
  52. $ attributes 。= "h" ;
  53. if ( $ attr & 0x04 )
  54. $ attributes 。= "s" ;
  55. if ( $ attr & 0x10 || $ attr & 0x4000 )
  56. $属性 = "d" ;
  57. if ( $ attr & 0x20 )
  58. $ attributes 。= "a" ;
  59. //ファイル名、パッキング前後のサイズ、CRC、属性を読み取ります
  60. $ファイル [ ] = 配列 (
  61. "ファイル名" => substr ( $ block 、 32 、 temp_getBytes ( $ block 、 26、2 ) ) 、
  62. "size_compressed" => $ packSize 、
  63. "size_uncompressed" => temp_getBytes ( $ block 、 11、4 ) 、
  64. "crc" => temp_getBytes ( $ block 、 16、4 ) 、
  65. 「属性」 => $ attributes 、
  66. ) ;
  67. } else {
  68. //このブロックがファイルでない場合、可能性を考慮に入れずにスキップします
  69. //余分なオフセットADD_SIZE
  70. $ flags = temp_getBytes ( $ block 、 3、2 ) ;
  71. if ( $ flags & 0x8000 ) {
  72. $ addSize = temp_getBytes ( $ブロック 、 7、4 ) ;
  73. fseek ( $ id 、 $ addSize 、 SEEK_CUR ) ;
  74. }
  75. }
  76. }
  77. fclose ( $ id ) ;
  78. //ファイルのリストを返します
  79. $ファイルを 返し ます 。
  80. }




GitHubにコメントを付けてコードを取得できます 。



文学



さて、いつものように、レビュー用の文献:







見込み



アーカイブからのファイルの読み取りに関しては、理論的には、UniquEからライブラリをリファクタリングすることでPHPで実行できますが、これはバージョン2.90より前に作成されたアーカイブにのみ適しています。 ライブラリは新しいアーカイブを読みません...しかし、あなたはあなた自身が500キロバイトのコードを理解する方法を理解しています。



All Articles