Pattern Matching and skipping

M

MattJ83

Ok guys,
Im not brushing your info to one side...but my plan was to get the code
running and then try and smarten it up....At the moment im just trying
to write something very basic that does the job.....which i almost
have.
I have a time constraint, so wanted to write some code to get the
information into the database and then i can start playing with the
code...

If theres just no way of oing what I would like with this piece of code
inparticular then i obviously have to go back to the drawing board and
look at it again......i just thought there should be some kind of way
to 'skip' over a /pattern/ to allow the script to keep running...
 
T

Tad McClellan

MattJ83 said:
i just thought there should be some kind of way
to 'skip' over a /pattern/ to allow the script to keep running...


There is a way to do that, and it has already been shown to you.

If it is not working, post another short and complete program that
we can run that "doesn't work", and we will help you fix it.

Have you seent the Posting Guidelines that are posted here frequently?
 
D

David Squire

MattJ83 said:
Ok guys,
Im not brushing your info to one side...but my plan was to get the code
running and then try and smarten it up....At the moment im just trying
to write something very basic that does the job.....which i almost
have.
I have a time constraint, so wanted to write some code to get the
information into the database and then i can start playing with the
code...

If theres just no way of oing what I would like with this piece of code
inparticular then i obviously have to go back to the drawing board and
look at it again......i just thought there should be some kind of way
to 'skip' over a /pattern/ to allow the script to keep running...

Sure there is:

if (/pattern/) {
# do whatever you want when there is a match
}
else {
# do what is appropriate when there isn't a match, which could be as
simple as "next;" to go on to the next loop iteration.
}

If you don't want to do anything when there is no match, you could just have

next if !/pattern/;

as the first line of your loop.

You need to be willing to actually understand what you are doing though.
This is a group for helping people with the Perl programming. Various
people here have been trying to nudge you towards a solution to your
problem, and towards better Perl programming practice in general.

I learn something every day from participating here.

On the other hand, perhaps you are looking for http://jobs.perl.org/


DS
 
M

MattJ83

There is a way to do that, and it has already been shown to you.

Tad, is this the If, elsif statements ? I gave it a try but it didn't
seem to like it - I don't think i was putting in the right stuff...

while (<LOG>) {
if (/updates table/) {
# do stuff <--- i was putting similar code to what i had
already done
}
elsif (/elapsed/) {
# do other stuff
}

Eg -
while (<LOG>) {
if (/updates table/) { #if updates table
in file
chomp; #take it out
my @lines = ( $info); ; #place it in my
@lines and in variable $info
foreach my $info (@lines) { #loop my $info in each
file
}
elsif (/elapsed/) {
chomp;
my @lines = ($_);
foreach my $elapsed1 (@lines) {
}


I thought i still needed 'my $info' and my $elapsed1 as i have the
INSERT statement call these variables to insert them into the database.
and the foreach loops it so that it looks at all of the logs doesn't
it?

This is how i read it.....but i know im probably wrong - i have studied
programming and know what its supposed to do in theory but in practice
im a bit rubbish!
 
D

David Squire

MattJ83 said:
Eg -
while (<LOG>) {
if (/updates table/) { #if updates table
in file

Not in the file, in the *line* from log currently being processed (which
is implicitly stored in $_)
chomp; #take it out

This doesn't take anything out, it just "chomps" $_, the current line,
i.e. it removes any end of line character
my @lines = ( $info); ; #place it in my
@lines and in variable $info

One: where does $info get declared and get a value? Two: why are you
placing this single scalar value in an array @lines? @lines is now an
array with a single element, $lines[0], which has the same value as $info.
foreach my $info (@lines) { #loop my $info in each

Why are you looping over an array that has one and only one element?

Why are you ending the loop here? This (unnecessary) loop has no content.
 
T

Tad McClellan

MattJ83 said:
Tad, is this the If, elsif statements ?

Yes.


I gave it a try but it didn't
seem to like it


Then you didn't do it right.

I thought I asked you for a short and complete program that we
can run that demonstrates that it "didn't like it"?


Have you seen the Posting Guidelines that are posted here frequently?

(that is NOT a rhetorical question.)

while (<LOG>) {
if (/updates table/) {
# do stuff <--- i was putting similar code to what i had
already done
}
elsif (/elapsed/) {
# do other stuff
}


Here is a short and complete program that *you* can run that
illustrates that it "likes it" just fine...


------------------------
#!/usr/bin/perl
use warnings;
use strict;

while (<DATA>) {
if (/updates table/) {
print "MATCHED: $_";
}
elsif (/elapsed/) {
print "MATCHED: $_";
}
else {
print "NO MATCH: $_";
}
}


__DATA__
nothing interesting here
this has updates table in it
nothing here either
now we have elapsed
now we don't
 
M

MattJ83

David said:
Sure there is:

if (/pattern/) {
# do whatever you want when there is a match
}
else {
# do what is appropriate when there isn't a match, which could be as
simple as "next;" to go on to the next loop iteration.
}

If you don't want to do anything when there is no match, you could just have

next if !/pattern/;

as the first line of your loop.

You need to be willing to actually understand what you are doing though.
This is a group for helping people with the Perl programming. Various
people here have been trying to nudge you towards a solution to your
problem, and towards better Perl programming practice in general.

I learn something every day from participating here.

On the other hand, perhaps you are looking for http://jobs.perl.org/


DS
What you have mentioned here is what i've been trying to do all day but
with out any success - im actually a bit happier now because i can see
i am on the right lines - I was trying to do this yesterday but i think
because my knowledge is still quite limited I didn't have enough belief
that i was doing the right thing.........

I admit that i was getting completely confused with all the loops in
that code! I'll have another look tomorrow at these items and see if i
can put them in the right place with the correct syntax.....

I do appreciate your help in guiding me through this code - so far i've
just had a book and been told to right a script!
 
T

Tad McClellan

MattJ83 said:
I do appreciate your help in guiding me through this code - so far i've
just had a book and been told to right a script!


Writing a script is not too difficult.

Righting a script is more difficult.
 
M

MattJ83

while (<DATA>) {
if (/updates table/) {
print "MATCHED: $_";
}
elsif (/elapsed/) {
print "MATCHED: $_";
}
else {
print "NO MATCH: $_";
}
}

I understand this but, when i am trying to do something similar for my
specific code im struggling.

while (<$LOG>) {
if (/updates table/) {
my @lines = $_;
foreach my $file (@lines) {
# }else { next if !(/updates table/);
# }

With the if, else statements they are blocking my declared variables
($file) so that the sql statement can't see them. Im getting an
explicit package error.

Surely, i have to find /updates table/ - put the results in array
@lines - allow $file to read this array and then place this $file in my
SQL statement. The curley brackets prevent this?
 
T

Tad McClellan

MattJ83 said:
I understand this but, when i am trying to do something similar for my
specific code im struggling.


If you post a SHORT AND COMPLETE PROGRAM THAT WE CAN RUN, then
we can surely help you.

Try doing that for once.

while (<$LOG>) {
if (/updates table/) {
my @lines = $_;
^^^^^^^^^^^^^^^

Why do you include that last line of code?

What do you think it does for you?

(it does nothing of use to you, so why is it there?)

foreach my $file (@lines) {


Q: How many elements can there be in the @lines array?

A: One.

There is no need for a loop that is guaranteed to iterate only once.
 
M

MattJ83

If you post a SHORT AND COMPLETE PROGRAM THAT WE CAN RUN, then
we can surely help you.

Try doing that for once.

ok, sorry.

#!/usr/central/bin/perl
use strict;
#use warnings;
use DBI;

my @filenames= </home/username/logs/*.log>;
foreach my $filename (@filenames) {
open my $LOG, '<', $filename or die "can't open $filename: $!\n";

while (<$LOG>) {
if (/updates table/) {
my $info = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed1 = $_;
{
while (<$LOG>) {
if (/conflicting|FASTSEARCH/) {
my $fast = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed2 = $_;
{

close($LOG) ;

my $dbh = DBI ->connect("dbi:Oracle:SERVER", "DATABASE", "PASSWORD")
or die "couldn't connect to database: $DBI::errstr\n";

$dbh->do("insert into LOGS values ('$filename', '$info', '$elapsed1',
'$fast', '$elapsed2')")
or die ("inserting data failure: $!\n");

$dbh->disconnect;
}}}}}}}}}}}}}
exit;

..log
Text updates table text
text
text elapsed text
text
FASTSEARCH text text
text elapsed
text

If you make multiple copies of this log file and remove the word
FASTSEARCH from one of the logs - the log will not be placed into the
database.
^^^^^^^^^^^^^^^

Why do you include that last line of code?

What do you think it does for you?

(it does nothing of use to you, so why is it there?)

Yep - its been removed - i saw what you mean't! Thanks.
 
E

Eric Schwartz

MattJ83 said:
ok, sorry.

Problem is, your code, as posted, relies on a database we don't know
about, and don't really want to set up anyway. So we can't run the
program, even if we wanted to. Also, you can put your data in your
program, after a __DATA__ literal, and read it with the predefined
DATA filehandle. perldoc perldata for more on this (see: "Special
Literals").
#!/usr/central/bin/perl
use strict;
#use warnings;

You should uncomment this line. If you don't know how to get rid of
the warnings it produces, then ask here, and we can help. But with it
off, you're basically asking us to help you find problems that
warnings can uncover for you. In other words, you're communicating to
us (whether you mean to or not) that our time is less important than
your computers'.

But, we don't have a database set up. So this makes it too hard to
use. Anyway, you just need to replace your database inserts with
print statements to make it easy to use.
my @filenames= </home/username/logs/*.log>;
foreach my $filename (@filenames) {
open my $LOG, '<', $filename or die "can't open $filename: $!\n";

while (<$LOG>) {
if (/updates table/) {
my $info = $_;
{
while (<$LOG>) {

This right here is, well, not to put too fine a point on it, dumb.
You already have a loop over the data. Logically, it's confusing to
start a new loop inside the old one. The usual problem is that when
the next iteration of the outer while() begins, the default assumption
is that it will read the next line after the previous iteration.
Putting another while loop inside it destroys that assumption, and
confuses debuggers to no end. This is very bad style; if you worked
for me, I wouldn't accept it, because the next person to look at your
code would be horribly confused.

Because of the way you've done it, you won't see that effect, but it's
more of an accident than anything else. You definitely should not use
this anywhere else; most of the people responding to you are trying to
communicate that this is a bad and confusing idea.

You don't need to start a brand-new while loop anyway; what your code
says is that you only look for "elapsed" after you find "updates
table", and you only look for "conflicting" or "FASTSEARCH" after you
find "elapsed". If this is the case, you can just do something like:

#/usr/bin/perl
use warnings;
use strict;

my ($info, $elapsed1, $fast, $elapsed2);
while(<DATA>) {
chomp;

if (/updates table/) {
print "found updates: [$_]\n";
$info = $_;
}
if (/elapsed/) {
print "found elapsed: [$_]... ";
if ($fast) {
print "setting elapsed2\n";
$elapsed2 = $_;
last;
} elsif ($info) {
print "setting elapsed1\n";
$elapsed1 = $_;
}
}
if (/conflicting|FASTSEARCH/) {
print "found fast: [$_]\n";
$fast = $_;
}
}

# do db stuff here for real
print "info: [$info] fast: [$fast] ".
"elapsed1: [$elapsed1] elapsed2: [$elapsed2]\n";

__DATA__
Text updates table text
text
text elapsed text
text
FASTSEARCH text text
text elapsed
text
__END__

Notice how I only loop over it once. Each time I find a line I might
be interested in, I check to see if I really am-- in the case of
finding "elapsed", you may want to set $elapsed1 or $elapsed2, so you
have to check if $fast or $info are set to determine which one to set.
$dbh->do("insert into LOGS values ('$filename', '$info', '$elapsed1',
'$fast', '$elapsed2')")
or die ("inserting data failure: $!\n");

Ugh, don't do that. Let DBI escape all the values for you. What if,
for instance, $filename contained the string

a', 'b', 'c', 'd', 'e'); delete from LOGS;

? Then your statement would look like:

$dbh->do("insert into LOGS values ('a', 'b', 'c', 'd', 'e'); delete from LOGS");

That's only the easiest way to screw you up; there are far nastier and
subtler ways to cause problems. Also, and this is NOT a Perl problem,
but since I'm feeling generous, I'll point it out anyway: you should
always list the columns when you do an insert, because (A) it's easier
to see what's going on when you read the code (you can look at it and
know what goes where) and (B) if the order of the fields ever changes,
you're screwed, and you don't know why.

Instead of trying to put in quote marks yourself, let DBI do it for
you with placeholders:

$dbh->do("INSERT INTO LOG (filename, info, start, fast, end)
VALUES (?, ?, ?, ?, ?)", undef,
$filename, $info, $elapsed1, $fast, $elapsed2);

perldoc DBI, and look for "Placeholders and Bind Values" to learn more.
$dbh->disconnect;
}}}}}}}}}}}}}

Ugh. This is butt-ugly. You can't tell by looking at this line which
brace corresponds to which if or while statement. If you wanted to
change the code later, you'll pretty much have to keep running it
until you get rid of all the syntax errors, instead of reading the
code to see what goes where. And even then, you're likely to put
something in the wrong place.
exit;

.log
Text updates table text
text
text elapsed text
text
FASTSEARCH text text
text elapsed
text

If you make multiple copies of this log file and remove the word
FASTSEARCH from one of the logs - the log will not be placed into the
database.

That's what your code says to do-- if you don't find FASTSEARCH, you
don't get to the part that updates the database. Isn't that what you
wanted? I'm not sure, based on your posts here, that you're entirely
clear on what it is you're trying to do here.

-=Eric
 
J

John W. Krahn

MattJ83 said:
ok, sorry.

#!/usr/central/bin/perl
use strict;
#use warnings;
use DBI;

my @filenames= </home/username/logs/*.log>;
foreach my $filename (@filenames) {
open my $LOG, '<', $filename or die "can't open $filename: $!\n";

while (<$LOG>) {
if (/updates table/) {
my $info = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed1 = $_;
{
while (<$LOG>) {
if (/conflicting|FASTSEARCH/) {
my $fast = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed2 = $_;
{

You don't need all that nesting:

for my $filename ( </home/username/logs/*.log> ) {
open my $LOG, '<', $filename or die "can't open $filename: $!\n";

my ( $info, $elapsed1, $fast, $elapsed2 );
while ( <$LOG> ) {
chomp;
$info = $_ if /updates table/;
$elapsed1 = $_ if defined $info and /elapsed/;
$fast = $_ if defined $elapsed1 and /conflicting|FASTSEARCH/;
if ( defined $fast and /elapsed/ ) {
$elapsed2 = $_;

close($LOG) ;

my $dbh = DBI ->connect("dbi:Oracle:SERVER", "DATABASE", "PASSWORD")
or die "couldn't connect to database: $DBI::errstr\n";

$dbh->do("insert into LOGS values ('$filename', '$info', '$elapsed1',
'$fast', '$elapsed2')")
or die ("inserting data failure: $!\n");

$dbh->disconnect;

}
}
}



John
 
T

Tad McClellan

MattJ83 said:
ok, sorry.

#!/usr/central/bin/perl
use strict;
#use warnings;


You lose all of "use warnings" benefits if you comment it out.
use DBI;

my @filenames= </home/username/logs/*.log>;
foreach my $filename (@filenames) {
open my $LOG, '<', $filename or die "can't open $filename: $!\n";

while (<$LOG>) {
if (/updates table/) {
my $info = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed1 = $_;
{
while (<$LOG>) {
if (/conflicting|FASTSEARCH/) {
my $fast = $_;
{
while (<$LOG>) {
if (/elapsed/) {
my $elapsed2 = $_;
{


I'm pretty sure I said this already:

Read the line once, test it many times.


So why are you reading the lines 4 times instead of once?


[snip]
}}}}}}}}}}}}}


I have never seen a line of Perl code like that in my 12 years
of Perl programming. It is a sure sign of some conceptual error
on your part.

You need to understand what you are doing, not just try random stuff...
 
P

Peter J. Holzer

Ugh. This is butt-ugly. You can't tell by looking at this line which
brace corresponds to which if or while statement.

Yup. The only time I've seen somebody seriously proposing closing
multiple blocks on the same line, he also proposed that the closing
braces match vertically with the start of the statement, like this:


if (...) {
while (...) {
for (...) {
if (...) {
if (...) {
...
} } } } }

(or maybe he put the opening braces on a line by their own, I don't
remember)

That way it is easy to see that each closing brace corresponds to the
start of a statement. However, they don't correspond to the statements
they seem to correspond to, which is probably confusing, too (I guess
there's a reason that very few people use this style :)).

hp
 
U

Uri Guttman

PJH> if (...) {
PJH> while (...) {
PJH> for (...) {
PJH> if (...) {
PJH> if (...) {
PJH> ...
PJH> } } } } }

PJH> That way it is easy to see that each closing brace corresponds to the
PJH> start of a statement. However, they don't correspond to the statements
PJH> they seem to correspond to, which is probably confusing, too (I guess
PJH> there's a reason that very few people use this style :)).

disregarding the actual brace or coding style there i feel there is
fundamentally something wrong when i see deeply nested code like that in
perl. one issue is that many time an if or else is just control flow
like next/last/return. those can usually be reduced to statement
modifiers which eliminate a block and all of its cruft. and many times i
see the else block being the control flow. if the associated condtional
is inverted, then you can lose the else (becomes a statement modifier
again) and the if code loses its indent and becomes straight line code
at the higher indent level. another trick is to wrap the deeper nested
code into a sub of its own just for clarity's sake (yes, it will slow
you down some but clarity is important too).

so the goal is to not need such deeply nested code. maybe 3-4 deep is as
far as i go before i do something (and that include using all those
block elimination techniques i mentioned) like factoring out an inner
sub.

in general, seeing code like that means the coder is not experienced
enough to know how to make code readable as well as correct. :)

uri
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,201
Messages
2,571,048
Members
47,651
Latest member
VeraPiw932

Latest Threads

Top