从文件中的 header 数据确定文件扩展名的设计

Design for determining file extension from header data in files

我有一个问题我已经想了两天了,但在网上(或这里)没有找到任何东西,所以我想我会 post 这个问题,看看有什么我得到的反馈。我在业余时间编写了一个小程序,它扫描文件目录(已删除文件扩展名)并读取每个文件的文件 headers 以找出每个文件的文件扩展名.到目前为止,我已经使用了一系列 if/else if 语句来满足我程序的 "pattern matching" 功能....

private String fileHeaderCheck()
{
    /* ----- PNG File Type Verification ----- */
    if (headerCheck.buffer[0] == 0x89 && headerCheck.buffer[1] == 0x50 && headerCheck.buffer[2] == 0x4E && headerCheck.buffer[3] == 0x47
        && headerCheck.buffer[4] == 0xD && headerCheck.buffer[5] == 0xA && headerCheck.buffer[6] == 0x1A && headerCheck.buffer[7] == 0xA)
    {
        return ".png";
    }

    /* ----- JPG File Type Verification ----- */
    else if (headerCheck.buffer[0] == 0xFF && headerCheck.buffer[1] == 0xD8 && headerCheck.buffer[2] == 0xFF && headerCheck.buffer[3] == 0xE0)
    {
        return ".jpg";
    }

    // And so on and so on through the whole list of file types I am checking for

    else
        return "unknown";           
}

headerCheck 是一个 Buffer object,其中包含一个 byte[],用于保存从文件的前 512 个字节读取的数据。我在这个模式匹配过程中合并了 25-30 种不同的文件类型,所以你可以看到函数很快就会变得又大又丑。我想做的是使用一个 ArrayList of Node objects,每个包含:1. 特定文件类型的 header 信息的 byte[],以及 2. 表示文件扩展类型的字符串, 然后遍历数组,比较 Node byte[] 和 headerCheck.buffer[],无论匹配什么元素,我都会将它的 fileType 附加到我在 headerCheck.buffer[] 中读取的文件的末尾从。我的节点 class 是这样设置的....

class Node  //if this is the "png" Node 
{
    byte[] metadata;
    String fileType;

    Node()
    {
        this.metadata = new byte[]{0x89, 0x50, 0x4E, 0x47, 0xD, 0xA, 0x1A, 0xA};
        this.fileType = ".png";
    }
}

我想不通的是如何初始化ArrayList。我是否必须 create/instantiate 25-30 个单独的节点 classes 和预配置的数据只是为了初始化 ArrayList 然后将它们添加到其中?这似乎比所有 if/else if 语句或开关更糟糕......第二个问题是,一旦我用所有节点 object 实例化了我的 ArrayList,我就无法想象如何遍历它byte[] 以便将其与 headerCheck.buffer[] 进行比较。我正在突破我的知识界限,所以对于这些问题我没有代码,因为我不知道从哪里开始编写这些东西。我正在考虑 double-nested for 循环,但我还没有走那么远,因为我还没有找到初始化 ArrayList 的有效方法。我什至研究了一些设计模式作为可能的补救措施,但无济于事。任何帮助将不胜感激...谢谢

迭代方法(类似这些):

boolean match;

for (int x = 0; x < pattens.get(metadata).size(); x++)
{
    if (headerCheck.buffer[x] != patterns.get(metadata[x])
    {
        match = false;
        return;
    }   
    else
        return true;
}

您可以将节点 class 更改为如下所示:

class FilePattern {
    byte[] metadata ;
    String extension ;

    public FilePattern(byte[] m, String ext) {
        this.metadata = m ;
        this.extension = ext ;
    }

    /* add the getters here */
}

现在,您可以在向 ArrayList 添加元素的同时对其进行初始化,即

ArrayList<FilePattern> patterns = new ArrayList<FilePattern>();
patterns.add(new FilePattern(new byte[]{ ... }, "extension"));
.... // for other patterns.

接下来,遍历 patterns 的元素,提取元数据字节并与从文件读取的字节匹配并确定文件类型。

编辑:对于循环,你可以这样做:

for(FilePattern f : patterns)
    if(Arrays.equals(f.getMetadata(), headerCheck.buffer))
        System.out.println("Found file with extension : ", f.getExtension());

您使用的方法非常复杂,导致代码难以维护。如果您同意修改整个设计,我想提出如下替代方法:

创建一个接口命名为 FileExtension :

interface FileExtension {
    public String getExtension(byte[] buffer);
}

创建包含用于确定文件扩展名的逻辑的 classes :

class PNGExtension implements FileExtension {

    @Override
    public String getExtension(byte[] buffer) {
        /* ----- PNG File Type Verification ----- */
        String fileType = null;
        if (buffer[0] == 0x89 && buffer[1] == 0x50 && buffer[2] == 0x4E && buffer[3] == 0x47 && buffer[4] == 0xD && buffer[5] == 0xA
                && buffer[6] == 0x1A && buffer[7] == 0xA) {
            fileType = ".png";
        }

        return fileType;
    }

}

class JPGExtension implements FileExtension {

    @Override
    public String getExtension(byte[] buffer) {
        /* ----- JPG File Type Verification ----- */
        String fileType = null;
        if (buffer[0] == 0xFF && buffer[1] == 0xD8 && buffer[2] == 0xFF && buffer[3] == 0xE0) {
            fileType = ".jpg";
        }

        return fileType;
    }

}

创建复合混凝土class,最终用于根据前512字节获取文件扩展名 :

class CompositeFileExtension implements FileExtension {

    private List<FileExtension> fileExtensions;

    public CompositeFileExtension() {
        //in the real world, this list can be populated through an IoC container
        fileExtensions = new ArrayList<FileExtension>();
        fileExtensions.add(new PNGExtension());
        fileExtensions.add(new JPGExtension());

    }

    @Override
    public String getExtension(byte[] buffer) {
        String fileExtension = null;
        for(FileExtension extension : fileExtensions) {
            if((fileExtension=extension.getExtension(buffer))!=null) {
                break;
            }
        }

        return fileExtension;
    }
}

最后,所有代码片段放在一起将按如下方式工作:

public class FileMapperDemo {
    public static void main(String[] args) {
        /*
         * 1. For each file in directory
         *    2. Read first 512 bytes into buffer array
         *       3. Create a new CompositeFileExtension object and pass it the buffer
         * 
         */

                     CompositeFileExtension fileExtensions = new CompositeFileExtension();

                     //4. get the file extension 
                     String fileExtension = fileExtensions.getExtension(buffer);

    }
}

这种方法的优点是,只要需要从缓冲区中推断出新的文件扩展名类型,您只需将新的 FileExtension 对象添加到 ArrayList。您不需要创建两个相似的 byte 数组(您在 example.One 中创建两个字节数组用于从数组读取的前 512 个字节,另一个用于与该数组进行比较)