Получить номер строки из preg_match_all () - PullRequest
13 голосов
/ 19 января 2011

Я использую PHP preg_match_all () для поиска строки, импортированной с помощью file_get_contents (). Регулярное выражение возвращает совпадения, но я хотел бы знать, по какому номеру строки эти совпадения найдены. Какая техника лучше для этого?

Я мог бы прочитать файл как массив и выполнить регулярное выражение для каждой строки, но проблема в том, что мое регулярное выражение сопоставляет результаты по возврату каретки (новые строки).

Ответы [ 9 ]

12 голосов
/ 04 ноября 2011

ну, это немного поздно, может быть, вы уже решили это, но я должен был это сделать, и это довольно просто.использование флага PREG_OFFSET_CAPTURE в preg_match вернет символьную позицию совпадения.давайте предположим $ charpos, так что

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

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

вуаля!

11 голосов
/ 19 января 2011

Вы не можете сделать это только с помощью регулярных выражений. По крайней мере, не чисто. Что вы можете сделать, чтобы использовать флаг PREG_OFFSET_CAPTURE для preg_match_all и выполнить анализ всего файла после публикации.

Я имею в виду, что после того, как у вас есть массив строк совпадений и начальных смещений для каждой строки, просто посчитайте, сколько \r\n или \n или \r находятся между началом файла и смещением для каждого соответствия. Номер строки совпадения будет количеством различных терминаторов EOL (\r\n | \n | \r) плюс 1.

1 голос
/ 15 октября 2018

Использование preg_match_all с флагом PREG_OFFSET_CAPTURE необходимо для решения этой проблемы, комментарии к коду должны объяснить, какой тип массива preg_match_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, если используете разные переводы строки.

1 голос
/ 12 января 2017
$data = "Abba
Beegees
Beatles";

preg_match_all('/Abba|Beegees|Beatles/', $data, $matches, PREG_OFFSET_CAPTURE);
foreach (current($matches) as $match) {
    $matchValue = $match[0];
    $lineNumber = substr_count(mb_substr($data, 0, $match[1]), PHP_EOL) + 1;

    echo "`{$matchValue}` at line {$lineNumber}\n";
}

выход

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

(проверьте ваши требования к производительности)

1 голос
/ 19 января 2011

У вас есть пара вариантов, но ни один из них не является «простым»:

a) exec() и используйте системную команду grep, которая может сообщать номера строк:

exec("grep -n 'your pattern here' file.txt", $output);`

b) добавьте в файл, используя file_get_contents(), разделите его на массив строк, затем используйте preg_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-инъекции.С положительной стороны, вам не нужно придираться ко всему файлу, и вы сэкономите немного на накладных расходах памяти.

b) Вы защищены от атак с использованием инъекций оболочки, но при этом вы должны глотать все содержимоефайл.Если ваш файл большой, вы, вероятно, исчерпаете доступную память.

c) Вы вызываете регулярное выражение для каждой строки, что может привести к значительным накладным расходам, если вы имеете дело с большим количеством строк.

0 голосов
/ 23 декабря 2016
//Keep it simple, stupid

$allcodeline = explode(PHP_EOL, $content);

foreach ( $allcodeline as $line => $val ) :
    if ( preg_match("#SOMEREGEX#i",$val,$res) ) {
        echo $res[0] . '!' . $line . "\n";
    }
endforeach;
0 голосов
/ 19 мая 2016

Это работает, но выполняет новую preg_match_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_match_all для всего файла и поймать смещения совпадения.

$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;
                    }
                }
            }
        }
    }
}}
0 голосов
/ 09 августа 2011

Вы можете использовать preg_match_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++;
}
0 голосов
/ 19 января 2011

Я думаю, прежде всего, вам нужно прочитать $ 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
}
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...