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

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

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

 Alix Axel19 янв. 2011 г., 02:36
Я не вижу простого способа выполнить то, что вы хотите сделать ...
 scragz19 янв. 2011 г., 02:32
preg_split и подсчет строк в результатах? Это звучит глупо теперь, когда я это сказал.
 drudge19 янв. 2011 г., 02:31
Я собираюсь высказать предположение и сказать, что вы не сможете использоватьpreg_match_all за это.

Ответы на вопрос(9)

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

// 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;
                    }
                }
            }
        }
    }
}}

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

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

б) хлебать в файл, используя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++;
}

У каждого есть свои взлеты и падения

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

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

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

 bart31 июл. 2017 г., 22:00
Я думаю, что вы пропустили эту часть моего вопроса: я мог прочитать файл как массив и выполнить регулярное выражение для каждой строки, но проблема в том, что мое регулярное выражение сопоставляет результаты по возврату каретки (новые строки).
Решение Вопроса

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

вуаля!

прежде всего, вам нужно прочитать $ 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
}
}
 bart31 июл. 2017 г., 22:00
Я думаю, что вы пропустили эту часть моего вопроса: я мог прочитать файл как массив и выполнить регулярное выражение для каждой строки, но проблема в том, что мое регулярное выражение сопоставляет результаты по возврату каретки (новые строки).
//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;
 bart31 июл. 2017 г., 22:00
Я думаю, что вы пропустили эту часть моего вопроса: я мог прочитать файл как массив и выполнить регулярное выражение для каждой строки, но проблема в том, что мое регулярное выражение сопоставляет результаты по возврату каретки (новые строки).

С помощью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 если вы используете разные переводы строки.

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

Выход

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

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

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

Ваш ответ на вопрос