Мне нужно определить на 80%, является ли файл двоичным или текстовым, есть ли способ сделать это даже быстро и грязно / некрасиво в С #?
Как определить, является ли файл двоичным или текстовым в C #?
Ответы (11)
Я бы, вероятно, поискал множество управляющих символов, которые обычно присутствуют в двоичном файле, но редко в текстовом файле. Двоичные файлы, как правило, используют 0 достаточно, чтобы просто проверить множество 0 байтов, вероятно, было бы достаточно для обнаружения большинства файлов. Если вы заботитесь о локализации, вам также необходимо протестировать многобайтовые шаблоны.
Как уже говорилось, вам всегда может не повезти и вы получите двоичный файл, который выглядит как текст или наоборот.
Есть метод, называемый цепями Маркова. Просканируйте несколько файлов моделей обоих типов и для каждого байтового значения от 0 до 255 соберите статистику (в основном вероятность) следующего значения. Это даст вам профиль размером 64 Кбайт (256x256), с которым вы сможете сравнить свои файлы времени выполнения (в пределах% порогового значения).
Предположительно, именно так работает функция автоматического определения кодировки в браузерах.
Делюсь своим решением в надежде, что оно поможет другим, как помогает мне в этих сообщениях и на форумах.
Задний план
Я изучаю и ищу решение для того же. Однако я ожидал, что он будет простым или немного искаженным.
Однако большинство попыток предоставляют запутанные решения здесь, а также в других источниках и погружаются в Unicode, UTF-series, Спецификация, Кодировки, Порядок байтов. В процессе я также перешел на бездорожье и в таблицы Ascii и кодовые страницы тоже.
В любом случае, я придумал решение, основанное на идее чтения потока и проверки пользовательских управляющих символов.
Он построен с учетом различных подсказок и советов, представленных на форуме и в других местах, таких как:
- Проверьте наличие множества управляющих символов, например, ищите несколько последовательных нулевых символов.
- Проверьте UTF, Unicode, кодировки, спецификацию, порядок байтов и аналогичные аспекты.
Моя цель:
- Он не должен полагаться на порядок байтов, кодирование и другую более сложную эзотерическую работу.
- Это должно быть относительно легко реализовать и легко понять.
- Он должен работать со всеми типами файлов.
Представленное решение работает для меня на тестовых данных, включая mp3, eml, txt, info, flv, mp4, pdf, gif, png, jpg. Это дает ожидаемые результаты.
Как работает решение
Я полагаюсь на конструктор по умолчанию StreamReader чтобы делать то, что он может делать лучше всего в отношении определения характеристик, связанных с кодировкой файлов, который использует UTF8Encoding по умолчанию.
Я создал свою собственную версию проверки состояния настраиваемого элемента управления char, потому что Char.IsControl бесполезен. Он говорит:
Управляющие символы - это форматирующие и другие непечатаемые символы, такие как ACK, BEL, CR, FF, LF и VT. Стандарт Unicode назначает кодовые точки от \ U0000 до \ U001F, \ U007F и от \ U0080 до \ U009F для управляющих символов. Эти значения следует интерпретировать как управляющие символы, если их использование не определено приложением иначе. Среди прочего он рассматривает LF и CR как управляющие символы.
Это делает его бесполезным, поскольку текстовые файлы содержат как минимум CR и LF.
Решение
static void testBinaryFile(string folderPath)
{
List<string> output = new List<string>();
foreach (string filePath in getFiles(folderPath, true))
{
output.Add(isBinary(filePath).ToString() + " ---- " + filePath);
}
Clipboard.SetText(string.Join("\n", output), TextDataFormat.Text);
}
public static List<string> getFiles(string path, bool recursive = false)
{
return Directory.Exists(path) ?
Directory.GetFiles(path, "*.*",
recursive ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly).ToList() :
new List<string>();
}
public static bool isBinary(string path)
{
long length = getSize(path);
if (length == 0) return false;
using (StreamReader stream = new StreamReader(path))
{
int ch;
while ((ch = stream.Read()) != -1)
{
if (isControlChar(ch))
{
return true;
}
}
}
return false;
}
public static bool isControlChar(int ch)
{
return (ch > Chars.NUL && ch < Chars.BS)
|| (ch > Chars.CR && ch < Chars.SUB);
}
public static class Chars
{
public static char NUL = (char)0; // Null char
public static char BS = (char)8; // Back Space
public static char CR = (char)13; // Carriage Return
public static char SUB = (char)26; // Substitute
}
Если вы попробуете вышеуказанное решение, дайте мне знать, работает оно у вас или нет.
Другие интересные и связанные ссылки:
- О UTF и BOM в Unicode.org
- образцы файлов Unicode
- Как определить кодировку текстового файла и
- Определить кодировку файла в Csharp
new FileInfo(path).Length
, чтобы получить размер файла.
- person Jeremy Cook; 21.11.2016
Хотя это не является надежным, следует проверить, есть ли в нем двоичное содержимое.
public bool HasBinaryContent(string content)
{
return content.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n');
}
Потому что, если существует какой-либо управляющий символ (кроме стандартного \r\n
), то это, вероятно, не текстовый файл.
Если реальный вопрос здесь: «Можно ли читать и писать этот файл с помощью StreamReader / StreamWriter без изменений?», То ответ здесь:
/// <summary>
/// Detect if a file is text and detect the encoding.
/// </summary>
/// <param name="encoding">
/// The detected encoding.
/// </param>
/// <param name="fileName">
/// The file name.
/// </param>
/// <param name="windowSize">
/// The number of characters to use for testing.
/// </param>
/// <returns>
/// true if the file is text.
/// </returns>
public static bool IsText(out Encoding encoding, string fileName, int windowSize)
{
using (var fileStream = File.OpenRead(fileName))
{
var rawData = new byte[windowSize];
var text = new char[windowSize];
var isText = true;
// Read raw bytes
var rawLength = fileStream.Read(rawData, 0, rawData.Length);
fileStream.Seek(0, SeekOrigin.Begin);
// Detect encoding correctly (from Rick Strahl's blog)
// http://www.west-wind.com/weblog/posts/2007/Nov/28/Detecting-Text-Encoding-for-StreamReader
if (rawData[0] == 0xef && rawData[1] == 0xbb && rawData[2] == 0xbf)
{
encoding = Encoding.UTF8;
}
else if (rawData[0] == 0xfe && rawData[1] == 0xff)
{
encoding = Encoding.Unicode;
}
else if (rawData[0] == 0 && rawData[1] == 0 && rawData[2] == 0xfe && rawData[3] == 0xff)
{
encoding = Encoding.UTF32;
}
else if (rawData[0] == 0x2b && rawData[1] == 0x2f && rawData[2] == 0x76)
{
encoding = Encoding.UTF7;
}
else
{
encoding = Encoding.Default;
}
// Read text and detect the encoding
using (var streamReader = new StreamReader(fileStream))
{
streamReader.Read(text, 0, text.Length);
}
using (var memoryStream = new MemoryStream())
{
using (var streamWriter = new StreamWriter(memoryStream, encoding))
{
// Write the text to a buffer
streamWriter.Write(text);
streamWriter.Flush();
// Get the buffer from the memory stream for comparision
var memoryBuffer = memoryStream.GetBuffer();
// Compare only bytes read
for (var i = 0; i < rawLength && isText; i++)
{
isText = rawData[i] == memoryBuffer[i];
}
}
}
return isText;
}
}
Отличный вопрос! Я сам был удивлен, что .NET не предлагает для этого простого решения.
Следующий код помог мне различать изображения (png, jpg и т. Д.) И текстовые файлы.
Я только что проверил наличие последовательных нулей (0x00
) в первых 512 байтах в соответствии с предложениями Рона Уорхолика и Адама Брюсса:
if (File.Exists(path))
{
// Is it binary? Check for consecutive nulls..
byte[] content = File.ReadAllBytes(path);
for (int i = 1; i < 512 && i < content.Length; i++) {
if (content[i] == 0x00 && content[i-1] == 0x00) {
return Convert.ToBase64String(content);
}
}
// No? return text
return File.ReadAllText(path);
}
Очевидно, что это быстрый и грязный подход, однако его можно легко расширить, разбив файл на 10 фрагментов по 512 байт каждый и проверив 8 из них на наличие последовательных нулей (лично я бы вывел его как двоичный файл, если 2 или 3 из них совпадают - нули редко встречаются в текстовых файлах).
Это должно стать довольно хорошим решением для того, что вам нужно.
Быстро и грязно - использовать расширение файла и искать общие текстовые расширения, такие как .txt. Для этого вы можете использовать Path.GetExtension а> вызов. Все остальное не было бы классифицировано как «быстрое», хотя оно вполне может быть грязным.
Действительно, очень грязный способ - создать регулярное выражение, которое принимает только стандартный текст, знаки препинания, символы и пробельные символы, загрузить часть файла в текстовый поток, а затем запустить его с регулярным выражением. В зависимости от того, что квалифицируется как чистый текстовый файл в вашей проблемной области, отсутствие успешных совпадений будет указывать на двоичный файл.
Чтобы учесть Unicode, обязательно отметьте кодировку в вашем потоке как таковую.
Это действительно неоптимально, но вы сказали «быстро и грязно».
http://codesnipers.com/?q=node/68 описывает, как определить UTF. -16 по сравнению с UTF-8 с использованием метки порядка байтов (которая может появиться в вашем файле). Он также предлагает пройти через несколько байтов, чтобы проверить, соответствуют ли они шаблону многобайтовой последовательности UTF-8 (ниже), чтобы определить, является ли ваш файл текстовым.
- 0xxxxxxx ASCII ‹0x80 (128)
- 110xxxxx 10xxxxxx 2-байтовый> = 0x80
- 1110xxxx 10xxxxxx 10xxxxxx 3-байтовый> = 0x400
- 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 4-байтовый> = 0x10000
Как насчет другого способа: определить длину двоичного массива, представляющего содержимое файла, и сравнить его с длиной строки, которая у вас будет после преобразования данного двоичного массива в текст.
Если длина такая же, "нечитаемых" символов в файле нет, это текст (уверен на 80%).
Другой способ - определить кодировку файла с помощью UDE. Если кодировка обнаружена успешно, вы можете быть уверены, что это текст, в противном случае - двоичный. Потому что двоичный код не имеет кодировки.
Конечно, вы можете использовать другую библиотеку для определения кодировки, кроме UDE. Если библиотека определения кодировки достаточно хороша, этот подход может обеспечить 100% правильность.