Antonio Carlos Ribeiro
Antonio Carlos Ribeiro

Reputation: 87719

How to use regex to match file name and line number?

I need to parse all those lines using a single regex (preg_match_all) command:

at Object..test (resources/assets/js/tests/example.spec.js:423:23)
(/resources/assets/js/tests/example.spec.js:423)
/resources/assets/js/tests/example.spec.js:423
resources/assets/js/tests/example.spec.js:423

I should match (capturing groups) the file name and the line number

resources/assets/js/tests/example.spec.js
423

This is it (PHP) almost done: (?:\()([\/]*.*\/[^:]*):([0-9]+), but it's not matching all of them, here's it on regex101:

https://regex101.com/r/hM2sK8/15

enter image description here

Upvotes: 1

Views: 899

Answers (2)

anubhava
anubhava

Reputation: 785128

You can use this regex:

~(/?(?:[\w.-]+/)*[\w.-]+):(\d+)~

Updated RegEx Demo

RegEx Details:

  • (/?(?:[\w.-]+/)*[\w.-]+) matches and groups file resource path
  • : match literal :
  • (\d+) matches and groups number after :

Upvotes: 3

LSerni
LSerni

Reputation: 57388

If you also want to catch nonparenthesized filenames, you'd be better served by capturing "something resembling a full path followed by colon and a number":

<?php
    $test = <<<TEST
at Object..test (resources/assets/js/tests/example.spec.js:423:23)
(/resources/assets/js/tests/example.spec.js:423)
/resources/assets/js/tests/example.spec.js:423
resources/assets/js/tests/example.spec.js:423
TEST;
    preg_match_all('#([A-Za-z0-9/\._-]+):([1-9][0-9]*)#', $test, $gregs, PREG_SET_ORDER);

   print implode(array_map(
       function ($set) {
            return "Found {$set[1]} at line {$set[2]}\n";
       },
       $gregs
    ));

returns:

Found resources/assets/js/tests/example.spec.js at line 423
Found /resources/assets/js/tests/example.spec.js at line 423
Found /resources/assets/js/tests/example.spec.js at line 423
Found resources/assets/js/tests/example.spec.js at line 423

Getting unique file/line pairs

This might come in handy ($gregs is obtained as above):

    $pairs = array_map(
       function ($set) {
           if (0 !== strpos($set[1], '/')) {
               return [ "/{$set[1]}", $set[2] ];
           }
           return [ $set[1], $set[2] ];
       },
       $gregs
    );

    $pairs = array_map(
        'unserialize',
        array_unique(
            array_map(
                'serialize',
                $pairs
            )
        )
    );

    print_r($pairs);

Now $pairs only contains one pair.

Upvotes: 3

Related Questions