А именно, как бы вы отличили архивный (jar/rar/и т.д.) файл от текстового (xml/txt, независимого от кодировки)?
Определение типа двоичного/текстового файла в Java?
Ответы (11)
Гарантированного способа нет, но вот несколько возможностей:
Найдите заголовок в файле. К сожалению, заголовки зависят от файла, поэтому, хотя вы можете узнать, что это файл RAR, вы не получите более общий ответ, является ли он текстовым или двоичным.
Подсчитайте количество символьных и несимвольных типов. Текстовые файлы будут в основном состоять из буквенных символов, в то время как двоичные файлы, особенно сжатые, такие как rar, zip и т. д., будут иметь более равномерное представление байтов.
Ищите регулярно повторяющийся шаблон новых строк.
Использование класса файлов 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;
}
}
Выполнить file -bi {filename}. Если то, что он возвращает, начинается с 'text/', то оно не двоичное, в противном случае это так. ;-)
file -i {filename} и проверить, НЕТ ли charset=binary.
- person Steinway Wu; 30.05.2014
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;
}
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
Взгляните на библиотеку JMimeMagic.
jMimeMagic — это библиотека Java для определения MIME-типа файлов или потоков.
text/plain, это должен быть текстовый файл.
- person Daniel Hiller; 27.09.2017
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;
}
Если файл состоит из байтов 0x09 (табуляция), 0x0A (перевод строки), 0x0C (перевод страницы), 0x0D (возврат каретки) или от 0x20 до 0x7E, то это, вероятно, текст ASCII.
Если файл содержит любой другой управляющий символ ASCII, от 0x00 до 0x1F, за исключением трех вышеперечисленных, то, вероятно, это двоичные данные.
Текст UTF-8 следует очень специфическому шаблону для любых байтов со старшим битом, но кодировки с фиксированной длиной, такие как ISO-8859-1, этого не делают. UTF-16 часто может содержать нулевой байт (0x00), но только в любой другой позиции.
Вам понадобится более слабая эвристика для чего-либо еще.
Просто чтобы вы знали, я выбрал совсем другой путь. В моем случае есть только 2 типа файлов, вероятность того, что любой данный файл будет двоичным, высока. Так
- предположим, что файл двоичный, попробуйте сделать то, что должно быть сделано (например, десериализовать)
- поймать исключение
- обрабатывать файл как текстовый
- если это не удается, что-то не так с самим файлом
См. http://en.wikipedia.org/wiki/Magic_number_(программирование).
Вы можете попробовать 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