Определение типа двоичного/текстового файла в Java?

А именно, как бы вы отличили архивный (jar/rar/и т.д.) файл от текстового (xml/txt, независимого от кодировки)?


person yanchenko    schedule 07.03.2009    source источник
comment
Вопрос с подвохом - все они бинарные файлы.   -  person duffymo    schedule 06.10.2016


Ответы (11)


Гарантированного способа нет, но вот несколько возможностей:

  1. Найдите заголовок в файле. К сожалению, заголовки зависят от файла, поэтому, хотя вы можете узнать, что это файл RAR, вы не получите более общий ответ, является ли он текстовым или двоичным.

  2. Подсчитайте количество символьных и несимвольных типов. Текстовые файлы будут в основном состоять из буквенных символов, в то время как двоичные файлы, особенно сжатые, такие как rar, zip и т. д., будут иметь более равномерное представление байтов.

  3. Ищите регулярно повторяющийся шаблон новых строк.

person Aric TenEyck    schedule 07.03.2009

Использование класса файлов Java 7 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;
        }
    }
person rince    schedule 06.10.2016
comment
Это просто проверяет расширение файла, а не содержимое файла и, следовательно, бесполезно. - person ares; 01.12.2016
comment
Согласно документам, это зависит от того, что установлено. - person Adam; 21.09.2017
comment
@ares на самом деле вообще не просто проверяет расширение файла - person citizen conn; 27.03.2018

Выполнить file -bi {filename}. Если то, что он возвращает, начинается с 'text/', то оно не двоичное, в противном случае это так. ;-)

person Wilfred Springer    schedule 21.10.2009
comment
Похоже, что есть приложения/javascript и приложения/xml. Глядя здесь en.wikipedia.org/wiki/Internet_media_type, можно предположить, что это не так просто. - person Aaron; 24.12.2013
comment
Вы можете проверить с помощью file -i {filename} и проверить, НЕТ ли charset=binary. - person Steinway Wu; 30.05.2014
comment
Я должен сказать, что я не был полностью серьезен, когда я ответил выше. Текстовые файлы — это просто двоичные файлы, интерпретированные определенным образом. Если вы имели в виду, является ли что-то US-ASCII, вы можете проверить каждый байт и посмотреть, соответствует ли он вашему определению текста. Но, возможно, вы имели в виду любой тип кодировки символов. Это будет намного сложнее. Особенно, если вы учитываете те, которые используют, скажем, кодирование на основе энтропии (появляющиеся символы часто требуют меньшего количества бит). С другой стороны, если вы имели в виду все, что связано с US-ASCII, то изображение в кодировке Base64 также будет текстом? - person Wilfred Springer; 30.05.2014
comment
Из «Охоты на Снарка»: Так плакал посыльный, а команда отвечала. Это всего лишь условные знаки! - person Wilfred Springer; 30.05.2014
comment
В macOS я увидел с file --help, что -bI следует использовать для вывода типа mime и набора символов вместо -bi, который выводит только «обычный файл». - person K. Symbol; 05.06.2020

Я сделал это. Немного проще, но для языков на основе латиницы должно работать нормально, с настройкой соотношения.

/**
 *  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;
}
person Ondra Žižka    schedule 23.11.2012
comment
Спасибо за эту функцию. Одна вещь, которую мне трудно понять, это то, что происходит с возвращаемым значением: return (ascii + other) * 100 / other > 95; Которое, если я что-то не упустил, всегда будет возвращать true: в основном размер будет 1024, как и data.length и, следовательно, (ascii + other). Итак, если (ascii + other) * 100 == 102400, то 102400 / other > 95=›102400 > 95 * other=›other < 1078 Это означает, что должно быть более 1078 (из 1024) других, чтобы это возвращало false, что, очевидно, невозможно. Вы имели в виду?: (other / size * 100 > 95) Или я что-то пропустил? - person Inversus; 06.12.2013
comment
Классно классно. Да, в итоге я тоже на это пошел. Спасибо еще раз :) - person Inversus; 11.03.2014
comment
Сбой для UTF-16 LE, UTF-16 BE, UTF-8 с BOM - person rince; 06.10.2016
comment
@rince Это возможно, не стесняйтесь редактировать, чтобы улучшить спецификацию. Не уверен, что покрытие UTF 16 не раздует его до полноценного парсера контекстных символов... - person Ondra Žižka; 07.10.2016
comment
Ваш алгоритм обнаружил файлы javascript как двоичные. Я думаю, что большее количество двоичных сравнений может решить эту проблему. Между тем, @Michael von Wenckstern лучше работает над этим предложением в то время. - person Ratata Tata; 01.05.2018
comment
Возможно, что он определяет различные файлы как двоичные. Вам всегда нужно применять определенный набор используемых символов, конкретную кодировку и, что наиболее важно, ваше собственное представление о том, что такое текстовый файл. Например, является ли RTF текстовым файлом? Является ли PostScript текстовым файлом? Является ли содержимое, закодированное в формате MIME, состоящим из нескольких частей, текстовым файлом? ... - person Ondra Žižka; 15.10.2019
comment
@RatataTata, не могли бы вы предоставить свой файл JavaScript? - person Ondra Žižka; 15.10.2019

Взгляните на библиотеку JMimeMagic.

jMimeMagic — это библиотека Java для определения MIME-типа файлов или потоков.

person Daniel Hiller    schedule 07.03.2009
comment
Интересная библиотека, но чем она поможет? Он сообщает вам тип пантомимы, но не является ли он двоичным или текстовым. - person Adam; 21.09.2017
comment
@ Адам, я не понимаю твоего вопроса? Вы можете вывести из самого типа MIME, является ли файл двоичным или нет, верно? т.е. если тип text/plain, это должен быть текстовый файл. - person Daniel Hiller; 27.09.2017
comment
Хорошо, я был прямолинеен. Я имел в виду, что вам все равно придется писать дополнительную логику, чтобы интерпретировать MIME-тип как двоичный или текстовый. 'text/plain' - не единственный текстовый MIME-тип. - person Adam; 27.09.2017
comment
@ Адам, да, я тоже так думаю, что, например, application/json также является текстовым представлением. Но, ссылаясь на исходный вопрос, который был довольно неконкретным, я думаю, этого должно быть достаточно в качестве отправной точки :) - person Daniel Hiller; 27.09.2017

Я использовал этот код, и он довольно хорошо работает с текстом на английском и немецком языках:

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;
}
person Michael von Wenckstern    schedule 14.07.2012
comment
Идея интересная, но вместо replaceAll, которая без необходимости создает новую строку, я бы просто использовал цикл for для подсчета текстовых и нетекстовых символов. Ограничение в 1000 символов означает, что это не будет слишком дорого, но все же бесполезно. - person miniBill; 14.07.2012

Если файл состоит из байтов 0x09 (табуляция), 0x0A (перевод строки), 0x0C (перевод страницы), 0x0D (возврат каретки) или от 0x20 до 0x7E, то это, вероятно, текст ASCII.

Если файл содержит любой другой управляющий символ ASCII, от 0x00 до 0x1F, за исключением трех вышеперечисленных, то, вероятно, это двоичные данные.

Текст UTF-8 следует очень специфическому шаблону для любых байтов со старшим битом, но кодировки с фиксированной длиной, такие как ISO-8859-1, этого не делают. UTF-16 часто может содержать нулевой байт (0x00), но только в любой другой позиции.

Вам понадобится более слабая эвристика для чего-либо еще.

person Matthew    schedule 07.03.2009
comment
проверка любого появления 0x10..0x1f может быть самым простым вариантом для обнаружения двоичного файла? - person Stefan Haustein; 19.05.2021

Просто чтобы вы знали, я выбрал совсем другой путь. В моем случае есть только 2 типа файлов, вероятность того, что любой данный файл будет двоичным, высока. Так

  1. предположим, что файл двоичный, попробуйте сделать то, что должно быть сделано (например, десериализовать)
  2. поймать исключение
  3. обрабатывать файл как текстовый
  4. если это не удается, что-то не так с самим файлом
person yanchenko    schedule 15.03.2009
comment
Я думаю, что это лучший подход. Вам действительно важно, какой тип файла? Или вам все равно, можете ли вы делать с ним определенные вещи. Во многих случаях, если вы можете делать эти вещи, вам не нужно на самом деле знать, что это за тип. - person stackexchanger; 09.08.2017


Вы можете попробовать Apache Tika, я открыл запрос специально для этой функции

но на данный момент, я думаю, это может сработать... требуется более тщательное тестирование, также может возникнуть проблема с другими библиотеками типов пантомимы, в которых вам все еще действительно нужно сопоставление типа с тем, является ли он двоичным.

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
person xenoterracide    schedule 19.05.2021
comment
Возможно, вы также захотите проверить псевдонимы и родительские типы, это поможет вам обнаружить xml в основном текстовый или JavaScript основан на тексте - person Gagravarr; 19.05.2021

Вы можете попробовать инструмент DROID.

person Fabian Steeg    schedule 07.03.2009