我编写了以下方法来查看特定文件是仅包含ASCII文本字符还是控制字符除此之外。你能看一下这段代码,建议改进并指出疏忽吗?
逻辑如下:“如果文件的前500字节包含5个或更多控制字符 - 将其报告为二进制文件”
谢谢。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;
}
答案 0 :(得分:3)
x
似乎没有做任何事情。
如果文件小于500字节怎么办?
某些二进制文件的情况是,您可以为文件的前N个字节设置一个标头,其中包含一些对应用程序有用的数据,但二进制文件所用的库并不关心。您可以轻松地在前导码中使用500+字节的ASCII,然后是以下千兆字节的二进制数据。
如果无法打开或读取文件等,则应处理异常
答案 1 :(得分:3)
由于您将此类称为“isASCIIText”,因此您确切地知道您正在寻找什么。换句话说,它不是“isTextInCurrentLocaleEncoding”。因此,您可以更精确地使用:
if (thisByte < 32 || thisByte > 127) bin++;
编辑,很久以后 - 在评论中指出,这个简单的检查会被一个以很多新行开头的文本文件绊倒。使用“ok”字节表可能会更好,并且包含可打印字符(包括回车符,换行符和制表符,可能还有表单Feed,但我认为很多现代文档都不使用这些字符),然后检查桌子。
答案 2 :(得分:3)
如果文件大小小于500字节
行char it = (char) thisByte;
在概念上是可疑的,它混合了字节和字符概念,即。隐含地假设编码是一个字节=一个字符(它们不包括unicode编码)。特别是,如果文件是UTF-16编码,它将失败。
循环内部的返回(IMO稍差)忘记关闭文件。
答案 3 :(得分:1)
我注意到的第一件事 - 与您的实际问题无关,但您应该在finally
块中关闭输入流以确保它始终完成。通常这只是处理异常,但在你的情况下,你甚至不会在返回false
时关闭文件流。
除此之外,为什么要比较ISO控制字符?那不是“二进制”文件,这是一个“包含5个或更多控制字符的文件”。在我看来,一种更好的方法来解决这种情况,就是反转检查 - 编写一个isAsciiText
函数,该函数断言文件中的所有字符(或者如果你愿意的话,在前500字节中)都在一组已知良好的字节。
理论上,只检查文件的前几百个字节可能会让你遇到麻烦,如果它是一种复合文件(例如带有嵌入图片的文本),但实际上我怀疑每个这样的文件都会有二进制头文件数据一开始所以你可能还可以。
答案 4 :(得分:0)
答案 5 :(得分:0)
这不适用于linux或solaris的jdk安装包。他们有一个shell脚本开始,然后是bi数据blob。
为什么不使用像jMimeMagic(http://http://sourceforge.net/projects/jmimemagic/)这样的库来检查mime类型,并根据mimetype来决定如何处理文件。
答案 6 :(得分:0)
可以解析和比较ageinst 已知二进制文件头字节列表,例如provided here。
问题是,需要有一个仅包含二进制的标题的排序列表,而该列表可能根本不完整。例如,读取和解析某些 Equinox 框架 jar 中包含的二进制文件。不过,如果需要识别特定的文件类型,这应该可行。
如果您使用的是 Linux,对于磁盘上的现有文件,原生 file command execution 应该可以正常工作:
String command = "file -i [ZIP FILE...]";
Process process = Runtime.getRuntime().exec(command);
...
它会输出文件的信息:
...: application/zip; charset=binary
您可以使用 grep 或 Java 进一步过滤,具体取决于您是否只需要估计文件的二进制字符,或者您是否需要找出它们的 MIME
类型。
如果解析 InputStreams,比如档案内嵌套文件的内容,这将不起作用,不幸的是,除非求助于 shell-only 程序,如 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();
}
}