【问题标题】:Determining the extension type of an image file using binary使用二进制确定图像文件的扩展类型
【发布时间】:2014-12-08 15:05:52
【问题描述】:

我正在尝试编写一个函数来确定目录中的文件是否具有 gif/bmp/png/jpg 扩展名。现在我想我已经正确地编写了我的代码,直到列出目录中的文件并以二进制模式打开它们。

现在,我正在努力弄清楚如何确定图像的扩展名。现在我只专注于写我的“bool isGif();”函数... 要使用二进制确定文件是否为 .gif 扩展名,文件的前 6 个字节将包含 GIF87a 或 GIF89a。所以,要做到这一点,我会将文件的前六个字节读入一个数组,然后将它们与包含“GIF87a”或“GIF89a”的数组进行比较,对吗?

以下是我编写此代码的尝试。它给了我 2 个警告,但没有错误,它在程序中运行良好,但它从不输出目录包含 gif 的消息,我知道它确实如此,因为我把它放在那里......

getDir();

ifstream fin;

_finddata_t a_file;
intptr_t dir_handle;

dir_handle = _findfirst("*.*", &a_file);

//if (dir_handle == -1)
//{
    //return;
//}

while (_findnext(dir_handle, &a_file) == 0);
{
    fin.open(a_file.name, ios::in | ios::binary);

    if (!fin)
    {
        cout << endl << "Could not open the file."
            << " Attempting to open the next file." << endl;
        return false;
    }
    else
    {
        cout << "Files opened successfully."
            << " Processing through the directory." << endl;


            ifstream fl(a_file.name);
            fl.seekg(0, ios::end);
            size_t len = fl.tellg();
            char *ret = new char[len];
            fl.seekg(0, ios::beg);
            fl.read(ret, len);
            fl.close();

            char arr1[6] = { 'G', 'I', 'F', 8, 7, 'a' };
            char arr2[6] = { 'G', 'I', 'F', 8, 9, 'a' };

            if (ret == arr1 || arr2 )
            {
                cout << a_file.name << " has a .gif extension" << endl;
                return true;
            }


    }
}

好的,我想我现在已经接近了...这是对这个问题很重要的代码的更新/更改的 sn-p...我只是尝试使用 for 循环来读取前 6 个字节到一个字符串,所以我可以比较这些位以确定它是否是一个 gif,但我无法将字节输入到一个字符串中。

int i;
            int comp1, comp2;

            for (i = 0; i != 6; i++)
            {
                string gifStr;
                fin.read((char*)&a_file, i);

                gifStr(&a_file, i);
            }

            string gifStr1 = "GIF87a";
            string gifStr2 = "GIF89a";

            comp1 = strcmp( , gifStr1);

            if (comp1 == 0)
            {
                cout << a_file.name << " has a .gif extension" << endl;
            }

            comp2 = strcmp( , gifStr2);

            if (comp2 == 0)
            {
                cout << a_file.name << " has a .gif extension" << endl;
            }   

对不起,这个网站让我对回复和类似的事情有点困惑......哈哈。

【问题讨论】:

  • 一般情况下是不可能的。您需要扫描幻数签名。
  • 怎么不可能?您不能将单个字节读入一个数组,然后将这些字节与另一个字符数组进行比较,看看它们是否相等?这是一个 CSC 250 类的作业,所以它应该不会有太难的解决方案。
  • "... 所以应该不会有太难的解决方案" 那么祝你好运:-P ...
  • 无关:您认为您需要打开该文件多少次?我认为一次就足够了。
  • 您的代码泄漏内存,ret == arr1 不比较数组的文本内容。使用标准::字符串。不要将 strcmp 与 std::string 一起使用,使用 operator ==

标签: c++ image file visual-studio-2013 binary


【解决方案1】:

你可以查找你想要的每种图像类型的幻数。然后像下面那样比较它们(有点)。它只有几个幻数。我在 C++0x 刚出现时写了这个out.. 可能有更好的方法,但下面应该给出一个粗略的想法..

int ValidImage(std::uint8_t* ImageBytes)
{
    const static std::vector<std::uint8_t> GIFBytesOne = { 0x47, 0x49, 0x46, 0x38, 0x37, 0x61 };
    const static std::vector<std::uint8_t> GIFBytesTwo = { 0x47, 0x49, 0x46, 0x38, 0x39, 0x61 };
    const static std::vector<std::uint8_t> PNGBytes = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A };
    const static std::vector<std::uint8_t> BMPBytes = { 0x42, 0x4D };
    const static std::vector<std::uint8_t> JPGBytes = { 0xFF, 0xD8, 0xFF };
    const static std::vector<std::uint8_t> JPEGBytes = { 0x00, 0x00, 0x00, 0x0C, 0x6A, 0x50, 0x20, 0x20 };
    const static std::vector<std::uint8_t> TIFFMonoChrome = { 0x0C, 0xED };
    const static std::vector<std::uint8_t> TIFFOne = { 0x49, 0x20, 0x49 };
    const static std::vector<std::uint8_t> TIFFTwo = { 0x49, 0x49, 0x2A, 0x00 };
    const static std::vector<std::uint8_t> TIFFThree = { 0x4D, 0x4D, 0x00, 0x2A };
    const static std::vector<std::uint8_t> TIFFFour = { 0x4D, 0x4D, 0x00, 0x2B };
    const static std::vector<std::uint8_t> CompressedTGA = {0x0, 0x0, 0xA, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
    const static std::vector<std::uint8_t> DeCompressedTGA = {0x0, 0x0, 0x2, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};

    const static std::array<std::vector<std::uint8_t>, 13> All = {
        GIFBytesOne, GIFBytesTwo, PNGBytes, BMPBytes,
        JPGBytes, JPEGBytes, TIFFMonoChrome, TIFFOne,
        TIFFTwo, TIFFThree, TIFFFour, CompressedTGA,
        DeCompressedTGA
    };

    int I = 0;
    for (const auto& it : All)
    {
        if (std::equal(it.begin(), it.end(), ImageBytes))
            return I;
        ++I;
    }
    return -1;
}

然后:

    std::fstream hFile(FilePath, std::ios::in | std::ios::binary);

    if (!hFile.is_open())
    {
        throw std::invalid_argument("File Not Found.");
    }

    std::uint8_t Header[18] = {0};
    hFile.read(reinterpret_cast<char*>(&Header), sizeof(Header));
    hFile.seekg(0, std::ios::beg);

    IMAGE_TYPE type = ValidImage(Header);

其中IMAGETYPE 定义为:

enum IMAGE_TYPE {GIF = 0, PNG, BMP, JPG, JPEG, TIFF, TGA};

【讨论】:

  • 为什么你将向量通过值而不是引用传递给你的 lambda?为什么不使用std::equal?为什么要在基于范围的 for 循环中复制向量?
  • @NielKirk;代码是旧的?我不知道。我从来没有费心去修复它。它已经 3 岁了(写于 2011 年)。当时,我很糟糕,只对让事情正常工作和测试 C++0x 感兴趣。另外,我已经从 C# 移植了它,这可能是一个非常糟糕的主意,哈哈。我不知道..我不介意对糟糕的代码投反对票。我只是想向 OP 展示一个如何使用幻数来完成它的示例。
  • 这更好,但我仍然可以找到改进!
  • ImageBytes 未修改,因此应为 const。您的向量可以是 std::arrays 以更有效地使用内存。要么将所有子数组和 All 数组组合成一个巨大的初始化语句,要么将 All 的类型作为向量(或数组)的引用,这样它们就不会被复制并存储在内存中两次。你可以使用const auto&amp;
【解决方案2】:

罪魁祸首在这里:

if (ret == arr1 || arr2 )

你不能像这样测试 char 数组的相等性。另外 - 测试本身是不正确的。首先-如果可以这样检查-您必须将其更改为:

if (ret == arr1 || ret == arr2 )

但还是不行,您必须执行以下操作之一:

  • retarr1arr2 转换为std::string
  • 使用strcmp
  • 循环测试数组 1 char

从您的 cmets 和编辑到问题,您可以在这里做的最好的事情是阅读有关 strings 的信息。甚至可以查看一些documentation

【讨论】:

  • 好的,我想我已经很接近了。我改变了我的代码。所以,现在我只是想知道如何将字节读入字符串,因为它不会让我在 for 循环中直接进行分配......for (i = 0; i != 6; i++) { string gifStr; gifStr = fin.read((char*)&amp;a_file, i); } string gifStr1[7] = "GIF87a"; string gifStr2[7] = "GIF89a"; comp1 = strcmp( , gifStr1); if (comp1 == 0) { //confirmation message }
  • std::string ret_s(ret,6), arr1_s(arr1,6), arr2_s(arr2,6);。然后if(ret_s == arr1_s || ret_s == arr2_s)。甚至更好——创建arr1_s = "GIF87a" 等字符串。您需要#include&lt;string&gt; 才能使用字符串。字符串位于 std 命名空间内。您无需创建gifStr2[7]gifStr2 就足够了。
  • 所以你是说我不需要做一个for循环?我只是做gifStr_s(fin,6) 然后将它与gifStr1_s = "GIF87a";gifStr2_s = "GIF89a"; 比较?
  • @AdamChally 几乎是的。但是您不能将read 直接转换为string。像现在一样阅读 (fl.read(ret, len);),在阅读之后,通过执行 std::string gifStr(ret,6); 将内容转换为 std::string。真正阅读有关字符串的内容,您编写的代码越多,您就越需要它们。查看我帖子中的链接以获取资源。
  • 好的,我摆脱了 for 循环并执行了这个 fin.read((char*)&amp;a_file, 6);,它有效,但是当我尝试将它读入像这样的字符串 string gifStr;, gifStr(&amp;a_file, 6); 时,它显示“IntelliSense:调用没有适当的 operator() 或转换函数到指针函数类型的类类型的对象"
【解决方案3】:

以下代码的问题在于它将整个文件加载到内存中,即使您只想检查几个字节。这很浪费,但留作练习。

ifstream fl(a_file.name);
fl.seekg(0, ios::end);
vector<char> ret(fl.tellg());
fl.seekg(0, ios::beg);
fl.read(&ret[0], ret.size());
fl.close();

static const vector<string> gif_ids = { "GIF87a", "GIF89a" };
bool is_gif = false;
for (const auto& id : gif_ids)
{
    // check size first because the file may contain less data than the id
    if (ret.size() >= id.size() && std::equal(id.begin(), id.end(), ret.begin()))
    {
        // it's a gif!
        is_gif = true;
        break;
    }
}

【讨论】:

    猜你喜欢
    • 2019-09-15
    • 2019-07-25
    • 2010-10-11
    • 1970-01-01
    • 1970-01-01
    • 2010-09-08
    • 2018-01-27
    • 2013-03-20
    • 2015-01-15
    相关资源
    最近更新 更多