[英]Determining binary/text file type in Java?
即,您將如何從文本(xml/txt,與編碼無關)文件中區分存檔(jar/rar/etc.)文件?
沒有保證的方法,但這里有幾種可能性:
在文件中查找 header。 不幸的是,標頭是特定於文件的,因此雖然您可能會發現它是一個 RAR 文件,但您不會得到更通用的答案,即它是文本還是二進制文件。
計算字符與非字符類型的數量。 文本文件將主要是字母字符,而二進制文件——尤其是壓縮文件,如 rar、zip 等——往往會更均勻地表示字節。
尋找定期重復的換行符模式。
Using Java 7 Files class http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#probeContentType(java.nio.file.Path)
boolean isBinaryFile(File f) throws IOException {
String type = Files.probeContentType(f.toPath());
if (type == null) {
//type couldn't be determined, assume binary
return true;
} else if (type.startsWith("text")) {
return false;
} else {
//type isn't text
return true;
}
}
我做了這個。 稍微簡單一點,但對於基於拉丁語的語言,它應該可以正常工作,並調整比率。
/**
* Guess whether given file is binary. Just checks for anything under 0x09.
*/
public static boolean isBinaryFile(File f) throws FileNotFoundException, IOException {
FileInputStream in = new FileInputStream(f);
int size = in.available();
if(size > 1024) size = 1024;
byte[] data = new byte[size];
in.read(data);
in.close();
int ascii = 0;
int other = 0;
for(int i = 0; i < data.length; i++) {
byte b = data[i];
if( b < 0x09 ) return true;
if( b == 0x09 || b == 0x0A || b == 0x0C || b == 0x0D ) ascii++;
else if( b >= 0x20 && b <= 0x7E ) ascii++;
else other++;
}
if( other == 0 ) return false;
return 100 * other / (ascii + other) > 95;
}
運行file -bi {filename}
。 如果它返回的任何內容都以'text/'開頭,那么它是非二進制的,否則它是。 ;-)
看看JMimeMagic庫。
jMimeMagic 是一個 Java 庫,用於確定文件或流的 MIME 類型。
我使用了這段代碼,它非常適用於英語和德語文本:
private boolean isTextFile(String filePath) throws Exception {
File f = new File(filePath);
if(!f.exists())
return false;
FileInputStream in = new FileInputStream(f);
int size = in.available();
if(size > 1000)
size = 1000;
byte[] data = new byte[size];
in.read(data);
in.close();
String s = new String(data, "ISO-8859-1");
String s2 = s.replaceAll(
"[a-zA-Z0-9ßöäü\\.\\*!\"§\\$\\%&/()=\\?@~'#:,;\\"+
"+><\\|\\[\\]\\{\\}\\^°²³\\\\ \\n\\r\\t_\\-`´âêîô"+
"ÂÊÔÎáéíóàèìòÁÉÍÓÀÈÌÒ©‰¢£¥€±¿»«¼½¾™ª]", "");
// will delete all text signs
double d = (double)(s.length() - s2.length()) / (double)(s.length());
// percentage of text signs in the text
return d > 0.95;
}
如果文件由字節 0x09(制表符)、0x0A(換行)、0x0C(換頁)、0x0D(回車)或 0x20 到 0x7E 組成,那么它可能是 ASCII 文本。
如果文件包含任何其他 ASCII 控制字符,0x00 到 0x1F 不包括上述三個,那么它可能是二進制數據。
UTF-8 文本對於任何具有高位位的字節都遵循非常特定的模式,但像 ISO-8859-1 這樣的固定長度編碼則不遵循。 UTF-16 可以經常包含 null 字節 (0x00),但僅在其他 position 中。
對於其他任何事情,您都需要一個較弱的啟發式。
只是為了讓你知道,我選擇了一條完全不同的道路。 我的情況是,只有兩種類型的文件,任何給定文件都是二進制文件的可能性很高。 所以
你可以試試 Apache Tika,我已經為此功能專門開了一個請求
但就目前而言,我認為這可能有效......需要更徹底的測試,也可能存在其他 mime 類型庫的問題,在這些庫中,您仍然需要從類型到是否為二進制的映射。
var config = TikaConfig.getDefaultConfig();
var tika = new Tika( config );
var mimeTypes = config.getMimeRepository();
var mimetype = tika.detect(Path.of("my/foo"));
var rootType = mimeTypes.forName( mime ).getType().getType();
rootType.endsWith( "text" ); // text and x-text
你可以試試DROID工具。
基於 xenoterracide 的建議,這里是一個使用 Tika Core 的實現。
public boolean isText(byte[] contentBytes) {
Tika tika = new Tika();
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(contentBytes);
try {
Set<MediaType> mediaTypes = new HashSet<>();
MediaType mediaType = MediaType.parse(tika.detect(byteArrayInputStream));
MediaTypeRegistry mediaTypeRegistry = MediaTypeRegistry.getDefaultRegistry();
while(mediaType != null) {
mediaTypes.addAll(mediaTypeRegistry.getAliases(mediaType));
mediaTypes.add(mediaType);
mediaType = mediaTypeRegistry.getSupertype(mediaType);
}
return mediaTypes.stream().anyMatch(mt -> mt.getType().equals("text"));
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
您可以使用幾種不同的 Tika.detect 方法,具體取決於您是否從文件開始、具有文件名等。請參閱https://javadoc.io/static/org.apache.tika/tika-core/2.4 .1/org/apache/tika/Tika.html
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.