ファイルがバイナリかテキストかを80%で判断する必要がありますが、C#で迅速かつダーティ/ ugいことを行う方法はありますか?
通常、バイナリファイルには存在するがテキストファイルにはほとんど存在しない、豊富な制御文字を探します。バイナリファイルは0を十分に使用する傾向があるため、ほとんどのファイルをキャッチするには、多くの0バイトをテストするだけでおそらく十分です。ローカライズに関心がある場合は、マルチバイトパターンもテストする必要があります。
ただし、述べたように、あなたは常に不運で、テキストのように見える、またはその逆のバイナリファイルを取得できます。
マルコフ連鎖と呼ばれる方法があります。両方の種類のいくつかのモデルファイルをスキャンし、0〜255の各バイト値について、後続の値の統計(基本的に確率)を収集します。これにより、ランタイムファイルを(%のしきい値内で)比較できる64Kb(256x256)プロファイルが得られます。
おそらく、これがブラウザの自動検出エンコーディング機能の仕組みです。
これらの投稿やフォーラムで役立つので、他の人にも役立つことを願って、私のソリューションを共有してください。
私は同じための解決策を研究し、調査してきました。しかし、私はそれが単純であるか、わずかにねじれていると思っていました。
ただし、ほとんどの試みはここで複雑なソリューションと他のソースを提供し、Unicode、 TF-series 、BOM、エンコーディング、バイトオーダーに飛び込みます。その過程で、私もオフロードに行き、 Ascii Tables and Code pages にも行きました。
とにかく、ストリームリーダーとカスタム制御文字チェックのアイデアに基づいたソリューションを思いつきました。
フォーラムやその他の場所で提供されるさまざまなヒントを考慮して構築されています。
私の目標は:
提示されたソリューションは、mp3、eml、txt、info、flv、mp4、pdf、gif、png、jpgを含むテストデータで機能します。これまでのところ期待どおりの結果が得られます。
デフォルトで TF8Encoding を使用するファイルエンコーディング関連の特性の決定に関して、 StreamReaderのデフォルトコンストラクター に依存しています。
Char.IsControl は役に立たないように見えるため、独自のバージョンのカスタムコントロールchar条件のチェックを作成しました。それは言います:
制御文字とは、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
}
上記の解決策を試してみる場合、それがあなたのために働くかどうか教えてください。
その他の興味深く関連するリンク:
ここでの本当の質問が「このファイルを変更せずに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;
}
}
これは万全ではありませんが、バイナリコンテンツがあるかどうかを確認する必要があります。
public bool HasBinaryContent(string content)
{
return content.Any(ch => char.IsControl(ch) && ch != '\r' && ch != '\n');
}
制御文字が存在する場合(標準の\r\n
)、それはおそらくテキストファイルではありません。
簡単で汚いのは、ファイル拡張子を使用して、.txtなどの一般的なテキスト拡張子を探すことです。これには、 Path.GetExtension 呼び出しを使用できます。それ以外は汚れているかもしれませんが、実際には「クイック」として分類されません。
いい質問です! .NETがこのための簡単なソリューションを提供していないことに私は驚きました。
次のコードは、画像(png、jpgなど)とテキストファイルを区別するのに役立ちました。
連続したヌル(0x00
)Ron WarholicとAdam Brussの提案による最初の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);
}
明らかにこれは迅速で汚いアプローチですが、ファイルをそれぞれ512バイトの10個のチャンクに分割し、連続したnullがないかそれらの8つをチェックすることで簡単に拡張できます(個人的に、2の場合はバイナリファイルを推測しますまたはそれらの3つが一致する-テキストファイルではnullはまれです)。
それはあなたが何をしているのかについてかなり良い解決策を提供するはずです。
本当に本当に汚い方法は、標準のテキスト、句読点、記号、空白文字のみを使用する正規表現を作成し、テキストストリームにファイルの一部をロードしてから、正規表現に対して実行することです。問題のあるドメインで純粋なテキストファイルと見なされるものによっては、一致が成功しない場合はバイナリファイルが示されます。
ユニコードを説明するには、ストリームのエンコードをそのようにマークするようにしてください。
これは本当に最適ではありませんが、あなたは素早く汚いと言いました。
別の方法は、 [〜#〜] ude [〜#〜] を使用してファイルの文字セットを検出することです。文字セットが正常に検出された場合、それがテキストであることを確認できます。それ以外の場合はバイナリです。バイナリには文字セットがないためです。
もちろん、UDE以外の他の文字セット検出ライブラリを使用できます。ライブラリを検出する文字セットが十分であれば、このアプローチは100%の正確さを達成できます。
別の方法はどうですか:ファイルの内容を表すバイナリ配列の長さを決定し、指定されたバイナリ配列をテキストに変換した後の文字列の長さと比較します。
同じ長さの場合、ファイルに「読み取り不可能」なシンボルはなく、テキストです(80%になります)。
http://codesnipers.com/?q=node/68 バイトオーダーマーク(ファイルに表示される場合があります)を使用してUTF-16とUTF-8を検出する方法について説明します。また、ファイルがテキストファイルであるかどうかを判断するために、UTF-8マルチバイトシーケンスパターン(下記)に適合するかどうかを確認するために、いくつかのバイトをループすることをお勧めします。