Я использую PHP preg_match_all () для поиска строки, импортированной с помощью file_get_contents (). Регулярное выражение возвращает совпадения, но я хотел бы знать, по какому номеру строки эти совпадения найдены. Какая лучшая техника для этого?
Я мог читать файл как массив и выполнять регулярное выражение для каждой строки, но проблема в том, что мое регулярное выражение соответствует результатам возврата каретки (новые строки).
хорошо, это немного поздно, может быть, вы решили это сделать, но я должен был это сделать, и это довольно просто. используя флаг 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;
вуаля!
Вы не можете делать это только с регулярными выражениями. По крайней мере, не чисто. Что вы можете сделать, чтобы использовать флаг PREG_OFFSET_CAPTURE
для preg_match_all и провести синтаксический анализ всего файла целиком.
Я имею в виду, что после того, как у вас есть массив строк совпадений и начальные смещения для каждой строки, просто подсчитайте, сколько \r\n
или \n
или \r
находятся между началом файла и смещением для каждого совпадения. Номер строки совпадения будет состоять из числа различных терминаторов EOL ( \r\n
| \n
| \r
) плюс 1
.
У вас есть пара вариантов, но ни один из них не является «простым»:
a) exec()
и используйте команду grep
системы, которая может сообщать номера строк:
exec("grep -n 'your pattern here' file.txt", $output);`
b) Slurp в файле с помощью 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) Вы вызываете регулярное выражение для каждой строки, что будет иметь значительные накладные расходы, если вы имеете дело с большим количеством строк.
я думаю, в первую очередь, вам нужно прочитать $ 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 } }
$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
(проверьте свои требования к производительности)
Вы можете использовать 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++; }
-// 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++; }
Это работает, но выполняет новую 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; } } } } } }}
//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;