是否有一种快速,简单的方法来检查文件是ASCII还是二进制文件?
答案 0 :(得分:21)
这仅适用于PHP> = 5.3.0,并且不是100%可靠,但是嘿,它非常接近。
// return mime type ala mimetype extension
$finfo = finfo_open(FILEINFO_MIME);
//check to see if the mime-type starts with 'text'
return substr(finfo_file($finfo, $filename), 0, 4) == 'text';
答案 1 :(得分:3)
由于ASCII只是文本的编码,具有二进制表示,不是真的。您可以检查所有字节是否小于128,但即使这样也不能保证它被解码为ASCII。对于所有你知道它是一些疯狂的图像格式,或完全不同的文本编码,也没有使用所有八位。但是,它可能就足够了。如果你只是想检查一个文件是否是有效的ASCII,即使它不是一个“文本文件”,它肯定就足够了。
答案 2 :(得分:2)
您应该检查文件的mimetype,但是如果您愿意将文件加载到内存中,也许您可以使用以下内容检查缓冲区是否包含所有可打印字符:
<?php
$probably_binary = (is_string($var) === true && ctype_print($var) === false);
不完美,但在某些情况下可能会有所帮助。
答案 3 :(得分:1)
这种方式在我的项目中似乎没问题:
function probably_binary($stringa) {
$is_binary=false;
$stringa=str_ireplace("\t","",$stringa);
$stringa=str_ireplace("\n","",$stringa);
$stringa=str_ireplace("\r","",$stringa);
if(is_string($stringa) && ctype_print($stringa) === false){
$is_binary=true;
}
return $is_binary;
}
PS:抱歉,我的第一篇文章,我想在上一篇文章中添加评论:)
答案 4 :(得分:0)
在我的一个较旧的PHP项目中,我使用ASCII /二进制压缩。用户上载文件时,要求他们指定该文件是ASCII还是Binary。我决定修改代码以使服务器自动决定文件模式是什么,因为依靠用户的决定可能会导致压缩失败。我决定我的代码必须是绝对的,并且不要使用可能导致程序失败的技巧。我迅速整理了一些代码,进行了一些速度测试,然后决定在互联网上搜索是否有更快的代码示例来完成此任务。
Devin非常模糊的答案与我为完成该任务而编写的第一个代码有关。结果一般般。我发现在很多情况下,对于二进制文件,逐字节搜索速度更快。如果找到大于127的字节,则文件的其余部分可以忽略,整个文件都被视为二进制文件。话虽如此,您将必须读取文件的每个最后一个字节,以确定文件是否为ASCII。对于许多二进制文件而言,它似乎更快,因为二进制字节可能早于文件的最后一个字节,有时甚至第一个字节也将是二进制的。
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'ASCII',
2 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$handle = fopen($filename, 'rb');
for($i = 0; $i < $size; ++$i) {
$byte = fread($handle, 1);
if(ord($byte) > 127) {
fclose($handle);
return 2; // Binary
}
}
fclose($handle);
return 1; // ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
$loops = 1;
$x = 0;
$i = 0;
$start = microtime(true);
for($i = 0; $i < $loops; ++$i)
$x = filemode($filename);
$stop = microtime(true);
$duration = $stop - $start;
echo
'Filename: ', $filename, "\n",
'Filemode: ', $filemodes[filemode($filename)], "\n",
'Duration: ', $duration;
我的处理器不是很现代,但是我发现600Kb ASCII文件大约需要0.25秒才能完成。如果要在成百上千个大型文件上使用它,可能会花费很长时间。我决定尝试通过使缓冲区大于单个字节来加快速度,以一次读取文件而不是一个字节的块。使用块将使我能够立即处理更多文件,但不会过多地加载到内存中。如果我们正在测试的文件很大,并且我们打算将整个文件加载到内存中,则可能会占用过多的内存并导致程序失败。
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'ASCII',
2 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$buffer_size = 256;
$chunks = ceil($size / $buffer_size);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
$buffer_length = strlen($buffer);
for($byte = 0; $byte < $buffer_length; ++$byte) {
if(ord($buffer[$byte]) > 127) {
fclose($handle);
return 2; // Binary
}
}
}
fclose($handle);
return 1; // ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
$loops = 1;
$x = 0;
$i = 0;
$start = microtime(true);
for($i = 0; $i < $loops; ++$i)
$x = filemode($filename);
$stop = microtime(true);
$duration = $stop - $start;
echo
'Filename: ', $filename, "\n",
'Filemode: ', $filemodes[filemode($filename)], "\n",
'Duration: ', $duration;
速度差异非常明显,仅需0.15秒,而不是上一个功能的0.25秒,读取我的600Kb ASCII文件的速度快了十分之一秒。
现在,我将文件放在块中,我认为寻找替代方法来测试块中的二进制字符是一个好主意。我首先想到的是使用正则表达式查找非ASCII字符。
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'ASCII',
2 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$buffer_size = 256;
$chunks = ceil($size / $buffer_size);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
if(preg_match('/[\x80-\xFF]/', $buffer) === 1) {
fclose($handle);
return 2; // Binary
}
}
fclose($handle);
return 1; // ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
$loops = 1;
$x = 0;
$i = 0;
$start = microtime(true);
for($i = 0; $i < $loops; ++$i)
$x = filemode($filename);
$stop = microtime(true);
$duration = $stop - $start;
echo
'Filename: ', $filename, "\n",
'Filemode: ', $filemodes[filemode($filename)], "\n",
'Duration: ', $duration;
很棒! 0.02秒将我的600Kb文件视为ASCII文件,此代码似乎100%可靠。
现在我已经到了这里,我有机会检查其他用户部署的其他几种方法。
由davethegr8撰写的当今最受欢迎的答案使用了mimetype扩展名。首先,要求我在php.ini文件中启用此扩展。接下来,我针对没有文件扩展名的实际ASCII文件和没有文件扩展名的二进制文件测试了此代码。
这是我创建两个测试文件的方式。
<?php
$handle = fopen('E:\ASCII', 'wb');
for($i = 0; $i < 128; ++$i) {
fwrite($handle, chr($i));
}
fclose($handle);
$handle = fopen('E:\Binary', 'wb');
for($i = 0; $i < 256; ++$i) {
fwrite($handle, chr($i));
}
fclose($handle);
这是我测试两个文件的方式...
<?php
$filename = 'E:\ASCII';
$finfo = finfo_open(FILEINFO_MIME);
echo (substr(finfo_file($finfo, $filename), 0, 4) == 'text') ? 'ASCII' : 'Binary';
哪个输出:
二进制
和...
<?php
$filename = 'E:\Binary';
$finfo = finfo_open(FILEINFO_MIME);
echo (substr(finfo_file($finfo, $filename), 0, 4) == 'text') ? 'ASCII' : 'Binary';
哪个输出:
二进制
此代码将我的ASCII文件和二进制文件都显示为二进制文件,这显然是不正确的,因此我必须找出导致模仿类型为“文本”的原因。对我来说,很明显,也许文本只是可打印的ASCII字符。所以我限制了ASCII文件的范围。
<?php
$handle = fopen('E:\ASCII', 'wb');
for($i = 32; $i < 127; ++$i) {
fwrite($handle, chr($i));
}
fclose($handle);
然后再次测试。
<?php
$filename = 'E:\ASCII';
$finfo = finfo_open(FILEINFO_MIME);
echo (substr(finfo_file($finfo, $filename), 0, 4) == 'text') ? 'ASCII' : 'Binary';
哪个输出:
ASCII
如果我降低范围,它将视为二进制。如果我扩大范围,再次将其视为二进制。
因此,最被接受的答案不会告诉您文件是否为ASCII,而是仅包含可读文本。
最后,我需要测试另一个对我的文件使用ctype_print的答案。我认为最简单的方法是使用我在MarcoA的代码中补充的代码。
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'ASCII',
2 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$buffer_size = 256;
$chunks = ceil($size / $buffer_size);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
$buffer = str_ireplace("\t", '', $buffer);
$buffer = str_ireplace("\n", '', $buffer);
$buffer = str_ireplace("\r", '', $buffer);
if(ctype_print($buffer) === false) {
fclose($handle);
return 2; // Binary
}
}
fclose($handle);
return 1; // ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
$loops = 1;
$x = 0;
$i = 0;
$start = microtime(true);
for($i = 0; $i < $loops; ++$i)
$x = filemode($filename);
$stop = microtime(true);
$duration = $stop - $start;
echo
'Filename: ', $filename, "\n",
'Filemode: ', $filemodes[filemode($filename)], "\n",
'Duration: ', $duration;
太好了! 0.2秒告诉我我的600Kb文件是ASCII。我知道我的大型ASCII文件仅包含可见的ASCII字符。似乎知道我的二进制文件是二进制文件。还有我的纯ASCII文件...二进制!
我决定阅读ctype_print的文档,其返回值定义为:
如果文本中的每个字符都将实际创建输出,则返回TRUE (包括空格)。如果文本包含控制字符,则返回FALSE 或根本没有任何输出或控制功能的字符。
与davethegr8的答案一样,此功能仅告诉您文本是否包含可打印的ASCII字符,而不告诉您文本是否实际为ASCII。这并不一定意味着MacroA是完全错误的,只是它们并不完全正确。与str_replace相比,str_ireplace速度较慢,仅替换这三个控制字符来测试ctype_print不足以知道字符串是否为ASCII。为了使该示例适用于ASCII,我们必须替换每个控制字符!
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'ASCII',
2 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$buffer_size = 256;
$chunks = ceil($size / $buffer_size);
$replace = array(
"\x00", "\x01", "\x02", "\x03",
"\x04", "\x05", "\x06", "\x07",
"\x08", "\x09", "\x0A", "\x0B",
"\x0C", "\x0D", "\x0E", "\x0F",
"\x10", "\x11", "\x12", "\x13",
"\x14", "\x15", "\x16", "\x17",
"\x18", "\x19", "\x1A", "\x1B",
"\x1C", "\x1D", "\x1E", "\x1F",
"\x7F"
);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
$buffer = str_replace($replace, '', $buffer);
if(ctype_print($buffer) === false) {
fclose($handle);
return 2; // Binary
}
}
fclose($handle);
return 1; // ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
这花了0.04秒钟告诉我我的600Kb文件是ASCII。
我相信所有这些测试并非完全没有用,因为它确实给了我一个更多的主意。为什么不向我的原始功能添加可打印文件模式!在我的600Kb可打印ASCII文件上,它的速度似乎慢了0.018秒,而在这里。
<?php
$filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'Printable',
2 => 'ASCII',
3 => 'Binary'
);
function filemode($filename) {
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
$printable = true;
$buffer_size = 256;
$chunks = ceil($size / $buffer_size);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
if(preg_match('/[\x80-\xFF]/', $buffer) === 1) {
fclose($handle);
return 3; // Binary
}
else
if($printable === true)
$printable = ctype_print($buffer);
}
fclose($handle);
return $printable === true ? 1 : 2; // Printable or ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
$loops = 1;
$x = 0;
$i = 0;
$start = microtime(true);
for($i = 0; $i < $loops; ++$i)
$x = filemode($filename);
$stop = microtime(true);
$duration = $stop - $start;
echo
'Filename: ', $filename, "\n",
'Filemode: ', $filemodes[filemode($filename)], "\n",
'Duration: ', $duration;
我还针对正则表达式测试了ctype_print,发现ctype_print更快一些。
$printable = preg_match('/[^\x20-\x7E]/', $buffer) === 0;
这是我的最后一个功能,其中查找可打印文本是可选的,缓冲区大小也是可选的。
<?php
const filemodes = array(
-2 => 'Unreadable',
-1 => 'Missing',
0 => 'Empty',
1 => 'Printable',
2 => 'ASCII',
3 => 'Binary'
);
function filemode($filename, $printable = false, $buffer_size = 256) {
if(is_bool($printable) === false || is_int($buffer_size) === false)
return false;
$buffer_size = floor($buffer_size);
if($buffer_size <= 0)
return false;
if(is_file($filename)) {
if(is_readable($filename)) {
$size = filesize($filename);
if($size === 0)
return 0; // Empty
if($buffer_size > $size)
$buffer_size = $size;
$chunks = ceil($size / $buffer_size);
$handle = fopen($filename, 'rb');
for($chunk = 0; $chunk < $chunks; ++$chunk) {
$buffer = fread($handle, $buffer_size);
if(preg_match('/[\x80-\xFF]/', $buffer) === 1) {
fclose($handle);
return 3; // Binary
}
else
if($printable === true)
$printable = ctype_print($buffer);
}
fclose($handle);
return $printable === true ? 1 : 2; // Printable or ASCII
}
else
return -2; // Unreadable
}
else
return -1; // Missing
}
// ==========
$filename = 'e:\test.txt';
echo
'Filename: ', $filename, "\n",
'Filemode: ', filemodes[filemode($filename, true)], "\n";