Java 如何检查文件是否为二进制文件?
我编写了以下方法,以查看特定文件是否仅包含ASCII文本字符,还是除此之外还包含控制字符。你能看一下这个代码,提出改进建议并指出疏忽吗 逻辑如下:“如果文件的前500个字节包含5个或更多控制字符,请将其报告为二进制文件” 多谢各位Java 如何检查文件是否为二进制文件?,java,file,binary,ascii,Java,File,Binary,Ascii,我编写了以下方法,以查看特定文件是否仅包含ASCII文本字符,还是除此之外还包含控制字符。你能看一下这个代码,提出改进建议并指出疏忽吗 逻辑如下:“如果文件的前500个字节包含5个或更多控制字符,请将其报告为二进制文件” 多谢各位 public boolean isAsciiText(String fileName) throws IOException { InputStream in = new FileInputStream(fileName); byte[] bytes
public boolean isAsciiText(String fileName) throws IOException {
InputStream in = new FileInputStream(fileName);
byte[] bytes = new byte[500];
in.read(bytes, 0, bytes.length);
int x = 0;
short bin = 0;
for (byte thisByte : bytes) {
char it = (char) thisByte;
if (!Character.isWhitespace(it) && Character.isISOControl(it)) {
bin++;
}
if (bin >= 5) {
return false;
}
x++;
}
in.close();
return true;
}
x
似乎没有任何作用
如果文件小于500字节怎么办
某些二进制文件的情况是,您可以为文件的前N个字节设置一个头,其中包含一些对应用程序有用的数据,但二进制文件所在的库并不关心这些数据。您可以很容易地在这样的前导码中包含500+字节的ASCII码,然后在接下来的千兆字节中包含二进制数据
如果文件无法打开或读取,则应处理异常,等等。我注意到的第一件事-与您的实际问题无关,但您应该在
finally
块中关闭输入流,以确保始终完成。通常这只处理异常,但在您的情况下,当返回false
时,您甚至不会关闭文件流
旁白:为什么要与ISO控制字符进行比较?这不是“二进制”文件,而是“包含5个或更多控制字符的文件”。在我看来,更好的方法是反转check-write一个isAsciiText
函数,该函数断言文件中的所有字符(或前500个字节中的字符,如果您愿意的话)都在一组已知良好的字节中
理论上,如果文件是某种类型的复合文件(例如,带有嵌入图片的文本),仅检查文件的前几百个字节可能会让您陷入麻烦,但实际上,我怀疑每一个这样的文件在开始时都会有二进制头数据,因此您可能没有问题
if (thisByte < 32 || thisByte > 127) bin++;
if(thisByte<32 | | thisByte>127)bin++;
编辑,很长一段时间之后——在一篇评论中指出,这个简单的检查会被一个以许多新行开头的文本文件绊倒。最好使用一个包含“ok”字节的表,并包含可打印字符(包括回车符、换行符和制表符,可能还有表单提要,尽管我不认为许多现代文档都使用这些字符),然后检查该表。这不适用于linux或solaris的jdk安装包。它们有一个shell脚本开始,然后是一个bi数据blob 为什么不使用一些库来检查mime类型,比如jMimeMagic()和基于mimetype的deside如何处理文件
charit=(char)thisByte
在概念上是可疑的,它混合了字节和字符概念,即隐式假设编码是一个字节=一个字符(它们不包括unicode编码)。特别是,如果文件是UTF-16编码的,则会失败您可以解析和比较ageinst中的已知二进制文件头字节列表,例如 问题是,需要一个只包含二进制头的排序列表,而这个列表可能根本不完整。例如,读取和解析某些Equinox框架jar中包含的二进制文件。但是,如果需要识别特定的文件类型,这应该是可行的 如果您在Linux上,对于磁盘上的现有文件,本机应该可以正常工作:
String command = "file -i [ZIP FILE...]";
Process process = Runtime.getRuntime().exec(command);
...
它将输出有关文件的信息:
...: application/zip; charset=binary
您可以使用grep或Java对其进行进一步过滤,这取决于您是否只需要估计文件的二进制字符,或者您是否需要找出它们的MIME
类型
不幸的是,如果解析输入流(如归档文件中嵌套文件的内容),这将不起作用,除非求助于纯shell程序(如unzip
),以避免创建临时解压缩文件
对于这一点,到目前为止,对检查前500个字节的粗略估计对我来说还可以,正如上面的例子所暗示的那样;我使用了Character.isWhitespace/isISOControl(char)
,而不是Character.isIdentifierIgnorable(codePoint)
,假设UTF-8
默认编码:
private static boolean isBinaryFileHeader(byte[] headerBytes) {
return new String(headerBytes).codePoints().filter(Character::isIdentifierIgnorable).count() >= 5;
}
public void printNestedZipContent(String zipPath) {
try (ZipFile zipFile = new ZipFile(zipPath)) {
int zipHeaderBytesLen = 500;
zipFile.entries().asIterator().forEachRemaining( entry -> {
String entryName = entry.getName();
if (entry.isDirectory()) {
System.out.println("FOLDER_NAME: " + entryName);
return;
}
// Get content bytes from ZipFile for ZipEntry
try (InputStream zipEntryStream = new BufferedInputStream(zipFile.getInputStream(zipEntry))) {
// read and store header bytes
byte[] headerBytes = zipEntryStream.readNBytes(zipHeaderBytesLen);
// Skip entry, if nested binary file
if (isBinaryFileHeader(headerBytes)) {
return;
}
// Continue reading zipInputStream bytes, if non-binary
byte[] zipContentBytes = zipEntryStream.readAllBytes();
int zipContentBytesLen = zipContentBytes.length;
// Join already read header bytes and rest of content bytes
byte[] joinedZipEntryContent = Arrays.copyOf(zipContentBytes, zipContentBytesLen + zipHeaderBytesLen);
System.arraycopy(headerBytes, 0, joinedZipEntryContent, zipContentBytesLen, zipHeaderBytesLen);
// Output (default/UTF-8) encoded text file content
System.out.println(new String(joinedZipEntryContent));
} catch (IOException e) {
System.out.println("ERROR getting ZipEntry content: " + entry.getName());
}
});
} catch (IOException e) {
System.out.println("ERROR opening ZipFile: " + zipPath);
e.printStackTrace();
}
}
不幸的是,当此算法将包含“this\r\nis\r\nonly\r\ntext”的文件分类为二进制文件时,将其标记为正确答案。@Ingo-true;最好检查控制字符与非控制字符的比例,并检查文本中常见的控制字符等特殊情况。当我输入这个答案时,我太年轻了:)