从文件中的 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 个字节,另一个用于与该数组进行比较)
我有一个问题我已经想了两天了,但在网上(或这里)没有找到任何东西,所以我想我会 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 个字节,另一个用于与该数组进行比较)