簡體   English   中英

確定 Java 中的二進制/文本文件類型?

[英]Determining binary/text file type in Java?

即,您將如何從文本(xml/txt,與編碼無關)文件中區分存檔(jar/rar/etc.)文件?

沒有保證的方法,但這里有幾種可能性:

  1. 在文件中查找 header。 不幸的是,標頭是特定於文件的,因此雖然您可能會發現它是一個 RAR 文件,但您不會得到更通用的答案,即它是文本還是二進制文件。

  2. 計算字符與非字符類型的數量。 文本文件將主要是字母字符,而二進制文件——尤其是壓縮文件,如 rar、zip 等——往往會更均勻地表示字節。

  3. 尋找定期重復的換行符模式。

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 中。

對於其他任何事情,您都需要一個較弱的啟發式。

只是為了讓你知道,我選擇了一條完全不同的道路。 我的情況是,只有兩種類型的文件,任何給定文件都是二進制文件的可能性很高。 所以

  1. 假設文件是二進制文件,嘗試做應該做的事情(例如反序列化)
  2. 捕捉異常
  3. 將文件視為文本
  4. 如果失敗,則文件本身有問題

你可以試試 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM