提问人:Fravadona 提问时间:7/3/2023 最后编辑:Fravadona 更新时间:7/3/2023 访问量:52
PHP:验证“文本”文件的行,同时提取一些统计信息?
PHP: validate the lines of a "text" file while extracting some stats at the same time?
问:
我有一个文件(来自 POST 请求),我想根据一些约束进行验证:
- 所有行只能由 ASCII 可打印字符组成。
- 必须至少有一个 XYZ 记录(以 开头的行)。
@XYZ
- 最多只能有 999999 个 XYZ 记录
为此,我创建了一个通用函数,该函数按块读取文件,并将每一行传递给回调进行验证:
/*
* Iterates over each line of the file, passing them to the callback function for validation.
* When the callback function returns false, or when there is an error,
* the validation process ends.
*
* @param string $filename The name of the file to validate.
* @param callable $callback The callback function to use for validating each line.
* @param string $line_delimiter The line-ending delimiter (default is "\n").
* @param integer $buffer_size The maximum number of bytes to read from the file at a time (default is 8192).
*
* @return Returns true when $callback returned true for each line, false if not, and NULL on error.
*
* @warning When $buffer_size is not large enough to contain a whole line, $callback will validate chunks of lines.
*/
function validate_file_lines($filename, $callback, $line_delimiter = "\n", $buffer_size = 8192)
{
$handle = fopen($filename, 'rb');
$is_valid = (false === $handle ? null : true);
$remainder = '';
while ( $is_valid && !feof($handle) )
{
$buffer = fread($handle, $buffer_size);
if ( false === $buffer )
{
$is_valid = null;
}
else
{
$lines_array = explode($line_delimiter, $buffer);
$lines_array_key_last = count($lines_array) - 1;
$lines_array[0] = $remainder . $lines_array[0];
if ( $lines_array_key_last !== 0 )
{
$remainder = $lines_array[$lines_array_key_last];
unset($lines_array[$lines_array_key_last]);
}
foreach ( $lines_array as $line )
{
$is_valid = $callback($line);
if ( ! $is_valid )
break;
}
}
}
@fclose($handle);
return $is_valid;
}
现在,我正在使用它来验证文件,例如:
HEAD good
@XYZ 1
@XYZ 1
%END
HEAD better
@XYZ 2 2
%END
$xyz_count = 0;
$xyz_min = 1;
$xyz_max = 999999;
$is_valid_line = function($line) use(&$xyz_count, $xyz_max) {
$is_valid = true;
if ( ctype_print($line) )
{
if ( substr($line, 0, 6) === '@XYZ ' )
{
++$xyz_count;
$is_valid = $xyz_count <= $xyz_max;
}
}
else if ( '' !== @$line[0] )
{
$is_valid = false;
}
return $is_valid;
};
var_dump(
validate_file_lines('file.txt', $is_valid_line) && $xyz_count >= $xyz_min
);
电流输出为:
bool(false)
虽然我期待:
bool(true)
我做错了什么?
旁白
SPL 是否提供任何用于遍历文件行的类?
答:
1赞
Reed
7/3/2023
#1
您需要是 5 个字符,而不是 6 个字符。您可以使用按行读取。这是一个可能有效的准系统解决方案。你的模式应该只是substr()
fgets()
r
此外,还可以添加调试打印以显示发生错误的位置。
<?php
$fh = fopen($filename, 'r');
$valid = true;
$xyz_count = 0;
while ($valid && $line = fgets($fh)){
if (!ctype_print($line))$valid = false;
if (substr($line, 0, 5) == '@XYZ ')$xyz_count++;
if ($xyz_count >= $xyz_max)$valid = false;
// if (!$valid)echo "LINE (fail): {$line}";
}
if ($xyz_count === 0)$valid = false;
fclose($fh);
评论
1赞
Fravadona
7/3/2023
谢谢@Reed,在代码中查找问题时,子字符串大小超出了我的眼睛。
0赞
Fravadona
7/3/2023
我一直在用 和 做一些基准测试,后者是最快的。我主要担心的是该文件可能是二进制文件;在这种情况下安全吗?fgets
stream_get_line
fread+explode
fgets
fgets
1赞
Reed
7/3/2023
@Fravadona,我没有考虑过二进制文件。我不知道,但我认为你的担忧是有道理的,而且胎化/爆炸可能更安全。在你的爆炸中,一定要考虑,因为如果没记错的话,某些系统/文件用来表示换行符。您可以为 fgets() 提供长度,但我觉得这违背了您的目的。explode(["\r\n", "\n"])
\r\n
评论
fread
fread()
while (($buffer = fread($handle)) !== false)
fgets()
它的fgets!不是 fread()。php.net/manual/en/function.fgets.phpsubstr($line, 0, 6) === '@XYZ '
@XYZ