Pythonを使用してファイル形式を定義する

背景



みなさんこんにちは。 最近、問題に遭遇しました。原因不明の理由により、メモリカードはLOST.DIRフォルダ内のすべてのファイルを拡張子なしでドロップし始めました。 長い間、写真、ビデオ、オーディオ、ドキュメントなど、さまざまな種類のファイルが500個以上蓄積されています。 自分でファイル形式を理解することは不可能だったので、プログラムでこの問題を解決する方法を探し始めました。







ソリューションを検索



Webサービスやプログラムの形で既製のソリューションを使用したくなかったため、すべてのファイルを調べて拡張機能を自動的にインストールするコンソールユーティリティを作成するというアイデアがありました。 このユーティリティの作成にはPythonが選択されました。 適切なモジュールとライブラリを検索しても、いくつかの理由で結果が得られませんでした。







  1. 開発者サポートの欠如
  2. 冗長機能
  3. Pythonの新しいバージョンのサポートの欠如
  4. 過度のコードの複雑さ


多くのライブラリの中で、libmagicライブラリのラッパーであるpython-magic(GitHubでほぼ1000スター)が際立っていました。 しかし、Windowsで使用するには、Unixライブラリ用のDLLが必要です。 このオプションは私には向いていませんでした。







問題解決



上記に基づいて、サードパーティのライブラリとモジュールを使用せずに、それらを使用せずに問題を解決することにしました。 このタスクを実装する方法に関する情報を短時間検索した後、唯一確実な方法は、ファイルの署名から形式を決定することでした。







ファイル署名は、ファイル形式の定義を提供するバイトのセットです。 署名の形式は16進表記で次のとおりです。







50 4D 4F 43 43 4D 4F 43
      
      





幸いなことに、インターネットには、さまざまな形式の多くの署名をホストする2つの良いサイトがあります。 目標は、 最も一般的な形式です。

判明したように、一部の署名は、Microsoft Officeファイルの署名など​​、さまざまなファイル形式に適しています。 これに基づいて、場合によっては適切なファイル拡張子のリストを返す必要があります。







 print(get("D:\\some_ms_office_document")) #  ['doc', 'ppt', 'xls']
      
      





また、署名は多くの場合、ファイルの先頭からオフセットされます(3GPメディアコンテナーファイルなど)。







1.データのリスト



データのリストの形式で、値が次の形式のオブジェクトの配列になる「データ」オブジェクトでJSONファイルを使用することが決定されました。







 {"format": "jpg", "offset": 0, "signature": ["FF D8 FF E0", "FF D8 FF E1", "FF D8 FF E2", "FF D8 FF E8"]}
      
      





どこで:

format-ファイル形式。

offset-ファイルの先頭からの署名オフセット。

signature-指定されたファイル形式に適した署名の配列。







2.ユーティリティの作成



必要なモジュールをインポートします。







 import os import json
      
      





データリストを読む:







 abspath = os.path.abspath(os.path.dirname(__file__)) data = json.loads(open(os.path.join(abspath, "data.json"), "r", encoding="utf-8").read())["data"]
      
      





データリストがアップロードされました。 ファイルをバイト単位で読み取ります。 最初の32バイトのみを読み取ります。これは、一般的な形式を識別する必要がなくなったためです。また、大きなファイルの完全な読み取りには多くの時間がかかります。







 file = open("path_to_the_file", "rb").read(32)
      
      





ファイル変数を印刷すると、次のようなものが表示されます。







 \x90\x00\x03\x00\x00\x00\x04
      
      





ここで、読み取りバイトを16進システムに変換する必要があります。







 hex_bytes = " ".join(['{:02X}'.format(byte) for byte in file])
      
      





次に、適切な形式が追加されるリストを作成します。







 out = []
      
      





そして今、最も興味深い部分:データリスト内のすべての可能な形式を通過するまで、ファイル形式を周期的に決定するデザインを作成します。







 for element in data: for signature in element["signature"]: offset = element["offset"]*2+element["offset"] if signature == hex_bytes[offset:len(signature)+offset].upper(): out.append(element["format"])
      
      





この行について:







 offset = element["offset"]*2+element["offset"]
      
      





バイトは文字列として表示され、2つの文字がバイトを担当するため、オフセットに2を掛けて、「バイト」の間にスペースの数を追加します。

そして、私たちに残された唯一のものは、変数outによって表される適切なフォーマットのリストを表示することです。







 print(out) #  ['_1', '_2']   ,    
      
      





おわりに



結局のところ 、さまざまなプロジェクトがファイル形式を認識する必要に直面しているので、 fleepGitHubページへのリンク)と呼ばれるPythonモジュールとしてソリューションをオープンソースでリリースすることにしました。 これで、標準のpython pipユーティリティを使用してモジュールをインストールできます。







 pip install fleep
      
      





また、GitHubプロジェクトページには、使用例とサポートされているファイル形式の完全なリストがあります。







ご清聴ありがとうございました!








All Articles