ssr1012
ssr1012

Reputation: 2589

Find line and column number in the text file using perl

I am trying to get the line and column number when the string exact matches in the file. Then I can able to get the line number and not column number.

Need to find \amp in the below string:

$str = '\begin{document}
    \title{Testing}
    It is important that the final model or models should make sense
    physically: at a minimum, this usually means that interactions should
    not be included without main effects nor higher-degree polynomial
    terms without their lower-degree relatives. Furthermore, if the model
    is to be used as a summary of the findings of one out of several
    studies bearing on the same phenomenon, main effects would usually be
    included whether significant or not.

    \begin{align}\label{equilibrium-disp-cyl}
    &G\left( {{\nabla ^{2}}{u_{r}} - \frac{2}{{{r^{2}}}}\frac{{\partial
{u_{\theta} }}}{{\partial \theta }} - \frac{{{u_{r}}}}{{{r^{2}}}}}
\right) \nonumber\\
\frac{1}{r}\frac{{\partial {u_{\theta} \amp}}}{{\partial \theta }} +
)\frac{1}{r}\frac{\partial }{{\partial \theta }}\left(
{\frac{{\partial {u_{r}}}}{{\partial r}} + \frac{{{u_{r}}}}{r} +
&G{\nabla ^{2}}{u_{z}} + ( {\lambda + G} )\frac{\partial }{{\partial
\end{align}
some para text continues....
    \begin{align}\label{equilibrium-disp-cyl}
&G\left( {{\nabla ^{2}}{u_{r}} - \frac{2}{{{r^{2}}}}\frac{{\partial
{u_{\theta} }}}{{\partial \theta }} - \frac{{{u_{r}}}}{{{r^{2}}}}}
\right) \nonumber\\
\frac{1}{r}\frac{{\partial {u_{\theta}}}}{{\partial \theta }} +
)\frac{1}{r}\frac{\partial }{{\partial \theta }}\left(
{\frac{{\partial {u_{r}}}}{{\partial r}} + \frac{{{u_{r}}}}{r} +
&G{\nabla ^{2}}{u_{z}} + ( {\lambda + G} \amp )\frac{\partial }{{\partial
\end{align}
some para text continues....
    \begin{align}\label{equilibrium-disp-cyl}
&G\left( {{\nabla ^{2}}{u_{r}} - \frac{2}{{{r^{2}}}}\frac{{\partial
{u_{\theta} }}}{{\partial \theta }} - \amp \frac{{{u_{r}}}}{{{r^{2}}}}}
\right) \nonumber\\
\frac{1}{r}\frac{{\partial {u_{\theta}}}}{{\partial \theta }} +
)\frac{1}{r}\frac{\partial }{{\partial \theta }}\left(
{\frac{{\partial {u_{r}}}}{{\partial r}} + \frac{{{u_{r}}}}{r} +
&G{\nabla ^{2}}{u_{z}} + ( {\lambda + G} \amp )\frac{\partial }{{\partial
\end{align}
';

My Code:

my $_pres = ();
while($str=~m/\\begin\{align\}((?:(?!\\end\{align\}).)*)\\end\{align\}/sg)
{
    $_pres = $`; my $nolabel = $&;
    if($nolabel=~m/\\amp/i)
    {
        my $nwpre = $`; $newpre = $_pres.$nwpre;

        my ($line) = ($newpre =~s/\n/\n/g)+1;
        print "L: $line - Found amp...!!!\n";
    }
}

Output:

 L: 8 - Found amp...!!!
 L: 21 - Found amp...!!!
 L: 26 - Found amp...!!!

Expected output:

 L: 7:nn - \\amp command found ...!!!

Could someone please guide me to get the column number and it would be appreciated also.

Upvotes: 1

Views: 742

Answers (2)

zdim
zdim

Reputation: 66881

I take it that the \\begin\{align\} and \\end\{align\} patterns are there to locate such passages (Latex's align environment) in a larger body of text.

Once you got that, break the rest into lines and finding \amp's location is then easy

use warnings;
use strict;

# ADDED another "\amp", to the line before last
my $str = '\begin{align}\label{equilibrium-disp-cyl}  
    ... [ suppressed for brevity ]    
\right) = 0, \amp
\end{align}
';

while ($str =~ m/\\begin\{align\} (.*?) \\end\{align\}/sgx)
{
    my @lines = split /\n/, $1; 
    for my $i (0..$#lines)
    {
        my $line = $lines[$i];

        if ($line =~ /(\\amp)/i) 
        {
            print  "Found '$1' -- ";
            printf "Line number: %3d, match start: %2d, match end: %2d\n",
                $i+1, $-[0], $+[0];
        }
    }
}

This uses @- (@LAST_MATCH_START) and @+ (@LAST_MATCH_END) arrays, which give offsets of the start and end of last successful submatches. See Regex related variables in perlvar. As there is only one match I use the first element, $-[0].

I use the simple (.*?) instead of an unneeded negative lookahead in the middle.

With your whole string restored (plus the extra \amp), the above prints

Found '\amp' -- Line number:   7, match start: 39, match end: 43
Found '\amp' -- Line number:  14, match start: 13, match end: 17

where I've added another \amp on the line-before-last, for a better test.


Clarification: We need the line number in the whole file and the column in the line where \amp is found, within the Latex's align environment (given by \begin{align}, \end{align}).

use warnings;
use strict;

my $file = 'doc.tex';
open my $fh, '<', $file or die "Can't open $file: $!";

while (<$fh>)
{
    if (/\\begin\{align\}/ .. /\\end\{align\}/)
    {   
        if (/(\\amp)/i) 
        {   
            print  "Found '$1' -- ";
            printf "Line number: %3d, match start: %2d, match end: %2d\n",
                $., $-[0], $+[0];
        }
    }
}

where the if statement uses the range operator to ensure that the /\\amp/ match is performed within the align environment only. The $. variable gives us the line number, and the use of @- and @+ is the same as explained above.

With the file doc.tex having the contents shown in the question, this prints

Found '\amp' -- Line number:  15, match start: 39, match end: 43
Found '\amp' -- Line number:  28, match start: 41, match end: 45
Found '\amp' -- Line number:  33, match start: 38, match end: 42
Found '\amp' -- Line number:  38, match start: 41, match end: 45

which I can confirm as correct locations in that text.

Upvotes: 2

Calvin Taylor
Calvin Taylor

Reputation: 694

Add an index check;

my $index = index($str, '\\amp');
print "L: $line:$index - \\amp command found...!!!\n";

however it's not enough to get 42,

[root@cal180 ~]# perl test.pl
L: 7:362 - \amp command found...!!!

Sounds like you may also want to parse brackets?

Upvotes: 1

Related Questions