Hatena::ブログ(Diary)

Life like a clown このページをアンテナに追加 RSSフィード Twitter

2009-08-18

ファイルタイプ判別関数群

先頭と末尾のマジックバイトを見る方法。この方法ってGIFとかPNGとか色々に応用できるので、その他のエントリも乱立する予感大。

はてなブックマーク - JPEG かどうかを判断する C のコード - tokuhirom’s blog.

はてなブログ に刺激されて.期待に応えて書いてみました:p

取りあえず,私が(ファイルフォーマットを)把握している限りのファイルタイプのチェック関数を作成してみました.作成したチェック関数は,BMP, PNG, GIF, JPEG, WMF, EMF, PDF, DOC, DOCX, XLS, XLSX, PPT, PPTX の 13 種類です.ただ,PNG, GIF, JPEG, PDF 辺りはファイル末尾もチェックしていますが,その他の多くはファイル先頭(もしくは途中)に出現する Magic Number とか Signature と呼ばれる固定値で判別しています.

Example

引数として与えたファイルの種別を判別するためのサンプルコードです.ただし,is_docx(), is_xlsx(), is_pptx() は,判別するために zlib を使用しているので,zlib Home Site からライブラリをインストール必要があります(UNIX 環境の場合は,大抵インストールされていると思いますが).また,g++ の場合は -lz オプションが必要となります.

#include <cstdlib>
#include <iostream>

// zlib がない場合は,officex.h 以外を使用する.
// その場合,clx/filetype.h はコメントアウト
#include "clx/filetype.h"
//#include "clx/image.h"
//#include "clx/pdf.h"
//#include "clx/office.h"

int main(int argc, char* argv[]) {
    if (argc < 2) std::exit(-1);
    
    std::string path(argv[1]);
    std::string type;

    // zlib がない場合は,is_docx, is_xlsx, is_pptx はコメントアウト.
    if (clx::is_bmp(path, clx::bmptype::os2)) type = "BMP (OS/2 1.x)";
    else if (clx::is_bmp(path, clx::bmptype::win)) type = "BMP (Windows)";
    else if (clx::is_png(path)) type = "PNG";
    else if (clx::is_gif(path)) type = "GIF";
    else if (clx::is_jpg(path)) type = "JPEG";
    else if (clx::is_wmf(path)) type = "WMF (APM)";
    else if (clx::is_wmf(path, false)) type = "WMF (Windows)";
    else if (clx::is_emf(path)) type = "EMF";
    else if (clx::is_pdf(path)) type = "PDF";
    else if (clx::is_doc(path)) type = "DOC (Binary)";
    else if (clx::is_xls(path)) type = "XLS (Binary)";
    else if (clx::is_ppt(path)) type = "PPT (Binary)";
    else if (clx::is_docx(path)) type = "DOCX (OpenXML)";
    else if (clx::is_xlsx(path)) type = "XLSX (OpenXML)";
    else if (clx::is_pptx(path)) type = "PPTX (OpenXML)";
    else type = "unknown file type";
    
    std::cout << argv[1] << ": " << type << std::endl;
    return 0;
}

ちなみに,WMF (Windows) と出力していますが,Windows においても使用される WMF ファイルのほとんどは APM (Aldus Placeable MetaHeader) と呼ばれる WMF ファイルで,Microsoft 純正の WMF ファイルが出現することはほとんどありません(出現しても対応できないアプリケーションも多い).

Declarations

全部の判別方法を説明しようかと思いましたが,長すぎるのでやめました.ソースコードは,CLX C++ Libraries からライブラリをダウンロードするか,以下のリンクから取得して下さい.ただし,image.h, pdf.h, office.h は単独で使用する事ができますが, officex.h は clx::unzip を使用しているので,関連するライブラリも取得する必要があります.

追記 Office ファイルの判別方法だけ別エントリで説明してみました. Microsoft Office ファイルの判別方法 - Life like a clown

namespace clx {
    namespace bmptype {
        enum { both = 0, os2, win };
    }

    // BMP (OS/2 1.x or Windows)
    // ビットマップファイル は OS/2 1.x と Windows 版でヘッダ形式が異なる.
    bool is_bmp(std::basic_istream<char>& in, int which = bmptype::both);
    bool is_bmp(const char* path, int which = bmptype::both);
    bool is_bmp(const std::basic_string<char>& path, int which = bmptype::both);

    // PNG
    bool is_png(std::basic_istream<char>& in);
    bool is_png(const char* path);
    bool is_png(const std::basic_string<char>& path);

    // GIF
    bool is_gif(std::basic_istream<char>& in);
    bool is_gif(const char* path);
    bool is_gif(const std::basic_string<char>& path);

    // JPEG
    bool is_jpg(std::basic_istream<char>& in);
    bool is_jpg(const char* path);
    bool is_jpg(const std::basic_string<char>& path);

    /* 
     * WMF (Placeable or Windows standard)
     * WMF は,通常 22Byte Placeable ヘッダ + 18Byte WMFヘッダ
     * と言う形を取る.22Byte Placeable ヘッダが存在しない
     * WMF ファイルも存在するが (APM ではない WMF ファイル)
     * それらは不正なファイルとして弾いてしまうアプリケーションも多い.
     */
    bool is_wmf(std::basic_istream<char>& in, bool apm = true);
    bool is_wmf(const char* path, bool apm = true);
    bool is_wmf(const std::basic_string<char>& path, bool apm = true);

    // EMF
    bool is_emf(std::basic_istream<char>& in);
    bool is_emf(const char* path);
    bool is_emf(const std::basic_string<char>& path);

    // PDF
    bool is_pdf(std::basic_istream<char>& in);
    bool is_pdf(const char* path);
    bool is_pdf(const std::basic_string<char>& path);

    // OLE2
    bool is_ole2(std::basic_istream<char>& in);
    bool is_ole2(const char* path);
    bool is_ole2(const std::basic_string<char>& path);

    // DOC (Microsoft Word)
    bool is_doc(std::basic_istream<char>& in);
    bool is_doc(const char* path);
    bool is_doc(const std::basic_string<char>& path);

    // XLS (Microsoft Excel)
    bool is_xls(std::basic_istream<char>& in);
    bool is_xls(const char* path);
    bool is_xls(const std::basic_string<char>& path);

    // PPT (Microsoft PowerPoint)
    bool is_ppt(std::basic_istream<char>& in);
    bool is_ppt(const char* path);
    bool is_ppt(const std::basic_string<char>& path);

    // DOCX (Microsoft Word 2007)
    bool is_docx(const std::basic_string<char>& path);
    bool is_docx(const char* path);

    // XLSX (Microsoft Excel 2007)
    bool is_xlsx(const std::basic_string<char>& path);
    bool is_xlsx(const char* path);

    // PPTX (Microsoft PowerPoint 2007)
    bool is_pptx(const std::basic_string<char>& path);
    bool is_pptx(const char* path);
}

引数には,多くの場合,判別したいファイルの入力ストリームかファイル名を指定します.ただし,is_docx(), is_xlsx(), is_pptx() の 3 種類は,現在のところ,ファイル名を受け取る関数しか提供していません (clx::unzip との兼ね合い).

References