2011-01-19 3 views
11

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

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

+1

Я собираюсь выбросить догадку и сказать, что вы не можете быть в состоянии для этого используйте 'preg_match_all'. – drudge

+0

preg_split и подсчет строк в результатах? Теперь это звучит глупо, что я это сказал. – scragz

+0

Я не вижу никакого простого способа выполнить то, что вы хотите сделать ... –

ответ

8

хорошо, возможно, поздно, может быть, вы решили это сделать, но я должен был это сделать, и это довольно просто. с использованием 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; 

вуаля!

10

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

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

1

я думаю, в первую очередь, вам нужно прочитать $ строки в массив, каждый элемент стоять на каждую линию, и они выглядят следующим образом:

$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 
} 
} 
+0

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

2

У вас есть пара вариантов, но ни один "простые":

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

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

б) Slurp в файле с помощью file_get_contents(), разделите его на массив строк, затем используйте preg_grep(), чтобы найти соответствующие строки.

$dat = file_get_contents('file.txt'); 
$lines = explode($dat, "\n"); 
$matches = preg_grep('/your pattern here/', $lines); 

с) Прочитайте файл в линии размера кусков, держать счет бегущей строки, и сделать свой матч шаблон на каждой строке.

$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++; 
} 

Каждый из них имеет свои взлеты и падения

а) вы вызываете внешнюю программу, и если ваш шаблон содержит все введенные пользователем данные, вы потенциально открываете себя до эквивалента раковины атака SQL-инъекций. С положительной стороны, вам не нужно прерывать весь файл и немного сэкономить на издержках памяти.

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

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

+0

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

0

Вы можете использовать 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

Это работает, но выполняет новую 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 на весь файл и 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; 
        } 
       } 
      } 
     } 
    } 
}} 
0
//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

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

1
$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 

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

Смежные вопросы