非公式には、「バイナリ」ファイル(オブジェクトファイル、画像、ムービー、実行可能ファイル、独自のドキュメント形式など)と「テキスト」ファイル(ソースコード、XMLファイル、HTMLファイル、電子メールなど)があることをほとんどの人が理解しています。
一般に、ファイルの内容を知って、それで何か便利なことができるようにする必要があり、エンコードが「バイナリ」または「テキスト」であれば、その観点を形成する必要はありません。そしてもちろん、ファイルはデータのバイトを保存するだけなので、それらはすべて「バイナリ」であり、「テキスト」はエンコーディングを知らなくても何も意味しません。それでも、「バイナリ」ファイルと「テキスト」ファイルについて話すことは依然として有用ですが、この不正確な定義で誰かを怒らせることを避けるために、私は「恐怖」引用符を使い続けます。
ただし、さまざまなファイルで動作するさまざまなツールがあり、実際には、ファイルが「テキスト」か「バイナリ」かによって異なることをしたいと考えています。この例は、コンソールにデータを出力するツールです。プレーンな「テキスト」は見栄えが良く、便利です。 「バイナリ」データは端末を台無しにし、一般的に見るのに役立ちません。 GNU grepは、コンソールへの一致を出力する必要があるかどうかを判断するときに、少なくともこの区別を使用します。
だから、問題は、ファイルが「テキスト」または「バイナリ」であるかどうかをどのように伝えるのですか?さらに制限することは、ファイルシステムのようなLinuxでどのように伝えますか?私はファイルの「タイプ」を示すファイルシステムのメタデータを知らないので、質問はさらに、ファイルの内容を検査することにより、「テキスト」または「バイナリ」であるかどうかをどのように見分けますか?また、簡単にするために、「テキスト」を、ユーザーのコンソールで印刷可能な文字を意味するように制限できます。そして特にimplement thisはどうでしょうか? (これはこのサイトで暗示されていると思いましたが、一般的に、これを行う既存のコードを指すと役立つと思います、私は指定する必要がありました)、私は実際に既存のプログラムを使用して何ができるこの。
file
コマンドを使用できます。ファイルに対して一連のテストを実行します(man file
)バイナリかテキストかを決定します。 Cからソースコードを実行する必要がある場合は、ソースコードを参照/借用できます。
file README
README: ASCII English text, with very long lines
file /bin/bash
/bin/bash: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), for GNU/Linux 2.2.5, dynamically linked (uses shared libs), stripped
ファイルの MIMEタイプ を決定できます
file --mime FILENAME
略記は、Linuxではfile -i
、macOSではfile -I
(大文字のi)です(コメントを参照)。
text/
で始まる場合はテキスト、それ以外の場合はバイナリです。唯一の例外はXMLアプリケーションです。ファイルタイプの最後で+xml
を検索することで、それらを一致させることができます。
さて、ファイル全体を調べているだけなら、すべての文字がisprint(c)
で印刷可能かどうかを確認してください。 Unicodeではもう少し複雑になります。
Unicodeテキストファイルを区別するために、 MSDNは何をすべきかについて素晴らしいアドバイスを提供します 。
その要点は、最初の4バイトまでを最初に検査することです。
_EF BB BF UTF-8
FF FE UTF-16, little endian
FE FF UTF-16, big endian
FF FE 00 00 UTF-32, little endian
00 00 FE FF UTF-32, big-endian
_
これでエンコーディングがわかります。次に、テキストファイルの残りの文字にiswprint(c)
を使用します。 UTF-8およびUTF-16の場合、単一の文字を可変バイト数で表すことができるため、データを手動で解析する必要があります。また、もしあなたが本当にアナルなら、あなたのプラットフォームで利用可能であれば、iswprint
のロケールバリアントを使用したいと思うでしょう。
Perlにはまともなヒューリスティックがあります。使用 -B
演算子でバイナリをテストします(その反対の-T
テキストをテストします)。テキストファイルを一覧表示する1行のシェルを次に示します。
$ find . -type f -print0 | Perl -0nE 'say if -f and -s _ and -T _'
(先行ドルなしのアンダースコアは正しいことに注意してください(RTFM)。)
現在のdir/subdirsのテキストファイル名を一覧表示するには:
$ grep -rIl ''
バイナリ:
$ grep -rIL ''
特定のファイルを確認するには、コマンドをわずかに変更します。
$ grep -qI '' FILE
次に、終了ステータス「0」は、ファイルがテキストであることを意味します。 '1'-バイナリ。確認できました:
$ echo $?
それは古いトピックですが、おそらく誰かがこれを役に立つと思うでしょう。何かがファイルかどうかをスクリプトで決定する必要がある場合は、次のようにできます。
if file -i $1 | grep -q text;
then
.
.
fi
これはファイルの種類を取得し、サイレントgrepを使用して、そのテキストかどうかを判断できます。
違いを認識しようとするほとんどのプログラムは、ファイルの最初のnバイトを調べて、それらのバイトall「テキスト」として修飾するかどうか(つまり、すべて印刷可能ASCII文字)の範囲内に収まりますか。UNIXライクなシステムでは、より細かく区別するために 'file'コマンドが常にあります。 。
1つの簡単なチェックは、\0
文字。テキストファイルにはありません。
前述のとおり、* nixオペレーティングシステムには、fileコマンド内にこの機能があります。このコマンドは、多くの一般的なファイル構造に含まれるマジックナンバーを定義する構成ファイルを使用します。
このファイルはマジックと呼ばれ、歴史的には/ etcに保存されていましたが、一部のディストリビューションでは/ usr/shareに存在する場合があります。マジックファイルは、ファイル内に存在することがわかっている値のオフセットを定義し、これらの場所を調べてファイルのタイプを判別できます。
マジックファイルの構造と説明は、関連するマニュアルページを参照して見つけることができます(man magic)
実装に関しては、 file.c 自体の中にありますが、読み取り可能なテキストかどうかを判断するファイルコマンドの関連部分は次のとおりです。
/* Make sure we are dealing with ascii text before looking for tokens */
for (i = 0; i < nbytes - 1; i++) {
if (!isascii(buf[i]) ||
(iscntrl(buf[i]) && !isspace(buf[i]) &&
buf[i] != '\b' && buf[i] != '\032' && buf[i] != '\033'
)
)
return 0; /* not all ASCII */
}