Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/php/228.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 从preg_match_all()获取行号_Php_Regex - Fatal编程技术网

Php 从preg_match_all()获取行号

Php 从preg_match_all()获取行号,php,regex,Php,Regex,我使用PHP的preg_match_all()来搜索使用file_get_contents()导入的字符串。正则表达式返回匹配项,但我想知道在哪个行号找到这些匹配项。实现这一目标的最佳技术是什么 我可以将文件作为数组读取,并对每一行执行正则表达式,但问题是我的正则表达式在回车(新行)之间匹配结果。仅使用正则表达式无法做到这一点。至少不干净。如何使用PREG\u match\u all的PREG\u OFFSET\u CAPTURE标志并对整个文件进行后期解析 我的意思是,在获得匹配字符串数组和每

我使用PHP的preg_match_all()来搜索使用file_get_contents()导入的字符串。正则表达式返回匹配项,但我想知道在哪个行号找到这些匹配项。实现这一目标的最佳技术是什么


我可以将文件作为数组读取,并对每一行执行正则表达式,但问题是我的正则表达式在回车(新行)之间匹配结果。

仅使用正则表达式无法做到这一点。至少不干净。如何使用PREG\u match\u all的
PREG\u OFFSET\u CAPTURE
标志并对整个文件进行后期解析


我的意思是,在获得匹配字符串数组和每个字符串的起始偏移量之后,只需计算文件开头和每个匹配的偏移量之间的
\r\n
\n
\r
数量。匹配的行号将是不同的EOL终止符的数量(
\r\n
\n
\r
)加上
1

我认为首先,您需要将$String读入一个数组中,每个元素代表每一行,并且如下所示:

$List=file($String);
for($i=0;$i<count($List),$i++){
if(preg_match_all()){;//your work here
echo $i;//echo the line number where the preg_match_all() works
}
}
$List=文件($String);

对于($i=0;$i您有几个选项,但没有一个是“简单的”:

a)
exec()
并使用系统
grep
命令,该命令可以报告行号:

exec("grep -n 'your pattern here' file.txt", $output);`
b) 使用
file\u get\u contents()
读取文件,将其拆分为一个行数组,然后使用
preg\u grep()
查找匹配的行

$dat = file_get_contents('file.txt');
$lines = explode($dat, "\n");
$matches = preg_grep('/your pattern here/', $lines);
c) 以行大小的块读取文件,保持行计数,并在每行上进行模式匹配

$fh = fopen('file.txt', 'rb');
$line = 1;
while ($line = fgets($fh)) {
     if (preg_match('/your pattern here/', $line)) {
         ... whatever you need to do with matching lines ...
     }
     $line++;
}
每个人都有自己的起伏

a) 您正在调用一个外部程序,如果您的模式包含任何用户提供的数据,那么您就有可能面临与SQL注入攻击相当的shell攻击。好的一面是,您不必在整个文件中发出咕噜声,这样可以节省一些内存开销

b) 您不会受到shell注入攻击,但您必须在整个文件中发出咕噜声。如果文件很大,可能会耗尽可用内存


c) 您每行调用一个正则表达式,如果您处理大量的行,这将有很大的开销。

您可以使用preg\u match\u all查找每个换行符的偏移量,然后将它们与已有的偏移量进行比较

// read file to buffer
$data = file_get_contents($datafile);

// find all linefeeds in buffer    
$reg = preg_match_all("/\n/", $data, $lfall, PREG_OFFSET_CAPTURE );
$lfs = $lfall[0];

// create an array of every offset
$linenum = 1;
$offset = 0;    
foreach( $lfs as $lfrow )
{
    $lfoffset = intval( $lfrow[1] );
    for( ; $offset <= $lfoffset; $offset++ )
        $offsets[$offset] = $linenum;   // offset => linenum
    $linenum++;
}
//将文件读取到缓冲区
$data=文件\获取\内容($datafile);
//在缓冲区中查找所有换行符
$reg=preg\u match\u all(“/\n/”,$data$lfall,preg\u OFFSET\u CAPTURE);
$lfs=$lfall[0];
//创建每个偏移量的数组
$linenum=1;
$offset=0;
foreach($lfs作为$lfrow)
{
$lfoffset=intval($lfrow[1]);
对于(;$offset linenum)
$linenum++;
}

现在有点晚了,也许你已经解决了这个问题,但我不得不这么做,而且相当简单。 在
PREG\u match
中使用
PREG\u OFFSET\u CAPTURE
标志将返回匹配的字符位置。 假设$charpos,那么

list($before) = str_split($content, $charpos); // fetches all the text before the match

$line_number = strlen($before) - strlen(str_replace("\n", "", $before)) + 1;

瞧!

这很有效,但在每一行上都会执行一个新的
preg\u match\u all
,这可能会非常昂贵

$file = file.txt;

$log = array();

$line = 0;

$pattern = '/\x20{2,}/';

if(is_readable($file)){

    $handle = fopen($file, 'rb');

    if ($handle) {

        while (($subject = fgets($handle)) !== false) {

            $line++;

            if(preg_match_all ( $pattern,  $subject, $matches)){

                $log[] = array(
                    'str' => $subject, 
                    'file' =>  realpath($file),
                    'line' => $line,
                    'matches' => $matches,
                );
            } 
        }
        if (!feof($handle)) {
            echo "Error: unexpected fgets() fail\n";
        }
        fclose($handle);
    }
}
或者,您可以在获得行号后读取文件,然后对整个文件执行
preg\u match\u all
,并对匹配偏移量进行catpure

$file = 'file.txt';
$length = 0;
$pattern = '/\x20{2,}/';
$lines = array(0);

if(is_readable($file)){

    $handle = fopen($file, 'rb');

    if ($handle) {

        $subject = "";

        while (($line = fgets($handle)) !== false) {

            $subject .= $line;
            $lines[] = strlen($subject);
        }
        if (!feof($handle)) {
            echo "Error: unexpected fgets() fail\n";
        }
        fclose($handle);

        if($subject && preg_match_all ( $pattern, $subject, $matches, PREG_OFFSET_CAPTURE)){

            reset($lines);

            foreach ($matches[0] as $key => $value) {

                while( list($line, $length) = each($lines)){ // continues where we left off

                    if($value[1] < $length){

                        echo "match is on line: " . $line;

                        break; //break out of while loop;
                    }
                }
            }
        }
    }
}}
$file='file.txt';
$length=0;
$pattern='/\x20{2,}/';
$lines=数组(0);
如果(是否可读($file)){
$handle=fopen($file,'rb');
如果($handle){
$subject=“”;
while(($line=fgets($handle))!==false){
$subject.=$line;
$lines[]=strlen($subject);
}
如果(!feof($handle)){
echo“错误:意外的fgets()失败\n”;
}
fclose($handle);
if($subject&&preg\u match\u all($pattern,$subject,$matches,preg\u OFFSET\u CAPTURE)){
重置($行);
foreach($将[0]匹配为$key=>$value){
而(list($line,$length)=每个($line)){//继续我们之前的内容
如果($value[1]<$length){
echo“匹配在线:”.$line;
break;//中断while循环;
}
}
}
}
}
}}
输出

`Abba` at line 1
`Beegees` at line 2
`Beatles` at line 3

(检查您的性能要求)

使用带有preg\u OFFSET\u CAPTURE标志的
preg\u match\u all
来解决此问题是必要的,代码注释应解释什么类型的数组
preg\u match\u all
返回以及如何计算行号:

// Given string to do a match with
$string = "\n\nabc\nwhatever\n\ndef";

// Match "abc" and "def" in a string
if(preg_match_all("#(abc).*(def)#si", $string, $matches, PREG_OFFSET_CAPTURE)) {
  // Now $matches[0][0][0] contains the complete matching string
  // $matches[1][0][0] contains the results for the first substring (abc)
  // $matches[2][0][0] contains the results for the second substring (def)
  // $matches[0][0][1] contains the string position of the complete matching string
  // $matches[1][0][1] contains the string position of the first substring (abc)
  // $matches[2][0][1] contains the string position of the second substring (def)

  // First (abc) match line number
  // Cut off the original string at the matching position, then count
  // number of line breaks (\n) for that subset of a string
  $line = substr_count(substr($string, 0, $matches[1][0][1]), "\n") + 1;
  echo $line . "\n";

  // Second (def) match line number
  // Cut off the original string at the matching position, then count
  // number of line breaks (\n) for that subset of a string
  $line = substr_count(substr($string, 0, $matches[2][0][1]), "\n") + 1;
  echo $line . "\n";
}

这将为第一个子串返回
3
,为第二个子串返回
6
。如果您使用不同的换行符,您可以将
\n
更改为
\r\n
\r

游戏后期,但我今天需要此功能,并且我意识到,答案可以组合成一个简单的解决方案。我还将我的用例的
\n
检查替换为
PHP\u EOL

// Get your matches
preg_match_all( '[YOUR REGEX HERE]', $data, $matches, PREG_OFFSET_CAPTURE );

// This is my loop format, yours may need to be different
foreach ( $matches[0] as $match ) {

    // Get the line number for the current match 
    list( $before ) = str_split( $data, $match[1] );
    $line_number = substr_count( $before, PHP_EOL ) + 1;
    echo $line_number;

}

我将抛出一个猜测,并说您可能无法使用
preg_match_all
进行此操作。preg_分割并计算结果中的行数?这听起来很愚蠢。我不认为有任何简单的方法可以完成您想要做的事情…我想您错过了我问题的这一部分:我可以将文件作为数组读取并执行正则表达式对于每一行,但问题是我的正则表达式匹配跨回车(新行)的结果。我想你错过了我问题的这一部分:我可以将文件作为数组读取并对每一行执行正则表达式,但问题是我的正则表达式匹配跨回车(新行)的结果。我想你错过了我问题的这一部分:我可以将文件作为数组读取,并对每一行执行正则表达式,但问题是我的正则表达式在回车(新行)中匹配结果。
// Given string to do a match with
$string = "\n\nabc\nwhatever\n\ndef";

// Match "abc" and "def" in a string
if(preg_match_all("#(abc).*(def)#si", $string, $matches, PREG_OFFSET_CAPTURE)) {
  // Now $matches[0][0][0] contains the complete matching string
  // $matches[1][0][0] contains the results for the first substring (abc)
  // $matches[2][0][0] contains the results for the second substring (def)
  // $matches[0][0][1] contains the string position of the complete matching string
  // $matches[1][0][1] contains the string position of the first substring (abc)
  // $matches[2][0][1] contains the string position of the second substring (def)

  // First (abc) match line number
  // Cut off the original string at the matching position, then count
  // number of line breaks (\n) for that subset of a string
  $line = substr_count(substr($string, 0, $matches[1][0][1]), "\n") + 1;
  echo $line . "\n";

  // Second (def) match line number
  // Cut off the original string at the matching position, then count
  // number of line breaks (\n) for that subset of a string
  $line = substr_count(substr($string, 0, $matches[2][0][1]), "\n") + 1;
  echo $line . "\n";
}
// Get your matches
preg_match_all( '[YOUR REGEX HERE]', $data, $matches, PREG_OFFSET_CAPTURE );

// This is my loop format, yours may need to be different
foreach ( $matches[0] as $match ) {

    // Get the line number for the current match 
    list( $before ) = str_split( $data, $match[1] );
    $line_number = substr_count( $before, PHP_EOL ) + 1;
    echo $line_number;

}