string retrieval issue

M

mbliven1

All,

I have searched the Perl documentation and the web for help on my issue
to no avail. Hoping someone here can help.

I have a perl CGI page pulling data from a two field pipe-delimited
flat file. The flat file itself will never be longer than 5 lines.
Anyway, in my script, I am opening the file, populating an array,
massaging the array, seeking the beginning of the flat file, truncating
the flat file, rewriting the contents of the array to the file and then
closing the file. Everything works as designed with the exception of
one issue. That's where I'm stuck. If the flat file looks like this:

Chicago Bears|NFC North
Detroit Lions|NFC North

then everything is great. However, if my file looks like this:
Chicago Bears|NFC North
Chicago Bulls|East Conference

then the script fails. In other words, if both lines begin with the
exact same string to the first space, then it will fail. It fails by
not writing the third element (Chicago Bulls) back to the array).
Ideas?
 
A

anno4000

All,

I have searched the Perl documentation and the web for help on my issue
to no avail. Hoping someone here can help.

I have a perl CGI page pulling data from a two field pipe-delimited
flat file. The flat file itself will never be longer than 5 lines.
Anyway, in my script, I am opening the file, populating an array,
massaging the array, seeking the beginning of the flat file, truncating
the flat file, rewriting the contents of the array to the file and then
closing the file. Everything works as designed with the exception of
one issue. That's where I'm stuck. If the flat file looks like this:

Chicago Bears|NFC North
Detroit Lions|NFC North

then everything is great. However, if my file looks like this:
Chicago Bears|NFC North
Chicago Bulls|East Conference

then the script fails. In other words, if both lines begin with the
exact same string to the first space, then it will fail. It fails by
not writing the third element (Chicago Bulls) back to the array).
Ideas?

Not without code. How, exactly, are you processing the CSV data?

It could be an interesting guessing game to reconstruct your erroneous
code from the symptoms it produces, but you got to be in the mood for
that.

Anno
 
P

Paul Lalli

That's where I'm stuck. If the flat file looks like this:

Chicago Bears|NFC North
Detroit Lions|NFC North

then everything is great. However, if my file looks like this:
Chicago Bears|NFC North
Chicago Bulls|East Conference

then the script fails. In other words, if both lines begin with the
exact same string to the first space, then it will fail. It fails by
not writing the third element (Chicago Bulls) back to the array).
Ideas?

.... You obviously did something wrong. How, precisely, are you
expecting anyone to tell you what you did wrong without seeing the code
you wrote?

Have you read the Posting Guidelines for this group? They're posted
here twice a week. I suggest you read them before replying.

Paul Lalli
 
M

mbliven1

Paul said:
... You obviously did something wrong. How, precisely, are you
expecting anyone to tell you what you did wrong without seeing the code
you wrote?

Have you read the Posting Guidelines for this group? They're posted
here twice a week. I suggest you read them before replying.

Paul Lalli

My bad about the post. I have now read the guidelines.

Here is the code:

open (FILE, "+<test.txt") || die ("test.txt wont open");
flock FILE, 2;
@lines = <FILE>;

$j=0;
$k=0;

foreach $element (@lines) {
($element1, $element2) = split(/\|/, $element);
@splitdata[$j] = $element1;
@splitdata[$k] = $element2;
$j = $j + 2;
$k=$k + 2;
}

foreach $p (param()) {
$formvalue = param($p);
@formvalue[$i] = $formvalue;

if ($formvalue eq "") {
@final[$i] = @splitdata[$i];
}
else {
@final[$i] = $formvalue;
}
$i++;
}

$x=0;
$y=0;
$z=0;

foreach (@final) {
$firstpart = @final[$x];
$secondpart = @final[$y];
$writevalue = "$firstvalue|$secondpart";
@finalwrite[$z] = $writevalue;
$x = $x + 2;
$y = $y + 2;
$z++;
}

seek FILE, 0, 0;
truncate FILE, 0;

print FILE @finalwrite;
close FILE;
# END OF CODE

as you may or may not tell, what I'm trying to do is allow a user to
enter a new value in a form on a CGI page. If they leave the field on
the main form blank, then the default value is the value already in the
flat file. Otherwise, the text entered into the form will replace the
old value. Yes, I know there is still some error-checking etc to do,
but I want to get this part working first. Thanks.
 
M

mbliven1

Paul said:
... You obviously did something wrong. How, precisely, are you
expecting anyone to tell you what you did wrong without seeing the code
you wrote?

Have you read the Posting Guidelines for this group? They're posted
here twice a week. I suggest you read them before replying.

Paul Lalli

My bad about the post. I have now read the guidelines.

Here is the code:

open (FILE, "+<test.txt") || die ("test.txt wont open");
flock FILE, 2;
@lines = <FILE>;

$j=0;
$k=0;

foreach $element (@lines) {
($element1, $element2) = split(/\|/, $element);
@splitdata[$j] = $element1;
@splitdata[$k] = $element2;
$j = $j + 2;
$k=$k + 2;
}

foreach $p (param()) {
$formvalue = param($p);
@formvalue[$i] = $formvalue;

if ($formvalue eq "") {
@final[$i] = @splitdata[$i];
}
else {
@final[$i] = $formvalue;
}
$i++;
}

$x=0;
$y=0;
$z=0;

foreach (@final) {
$firstpart = @final[$x];
$secondpart = @final[$y];
$writevalue = "$firstvalue|$secondpart";
@finalwrite[$z] = $writevalue;
$x = $x + 2;
$y = $y + 2;
$z++;
}

seek FILE, 0, 0;
truncate FILE, 0;

print FILE @finalwrite;
close FILE;
# END OF CODE

as you may or may not tell, what I'm trying to do is allow a user to
enter a new value in a form on a CGI page. If they leave the field on
the main form blank, then the default value is the value already in the
flat file. Otherwise, the text entered into the form will replace the
old value. Yes, I know there is still some error-checking etc to do,
but I want to get this part working first. Thanks.
 
P

Paul Lalli

My bad about the post. I have now read the guidelines.

Please read them again. Your next post should contain a SHORT but
COMPLETE snippet that demonstrates your problem. More importantly, it
should have strict and warnings enabled.
Here is the code:

open (FILE, "+<test.txt") || die ("test.txt wont open");
flock FILE, 2;
@lines = <FILE>;

$j=0;
$k=0;

foreach $element (@lines) {
($element1, $element2) = split(/\|/, $element);
@splitdata[$j] = $element1;
@splitdata[$k] = $element2;
$j = $j + 2;
$k=$k + 2;
}

Logic error. You're setting $splitdata[0] to $element1, and then
immediately overwriting it, setting $splitdata[0] to $element2. Then
you change both $j and $k to 2. So the next time, you set
$splitdata[2] to $element1, and immeidately overwrite it, setting
$splitdata[2] to $element[2].

Presumably, you meant to start one of $j or $k off at 1, not 0.

Paul Lalli
 
M

mbliven1

Paul said:
My bad about the post. I have now read the guidelines.

Please read them again. Your next post should contain a SHORT but
COMPLETE snippet that demonstrates your problem. More importantly, it
should have strict and warnings enabled.
Logic error. You're setting $splitdata[0] to $element1, and then
immediately overwriting it, setting $splitdata[0] to $element2. Then
you change both $j and $k to 2. So the next time, you set
$splitdata[2] to $element1, and immeidately overwrite it, setting
$splitdata[2] to $element[2].

Presumably, you meant to start one of $j or $k off at 1, not 0.

Paul Lalli

Actually, that was a typo on my part. In my actual code, I have $k=1;

I will enable strict and warnings. Thank you.

The code I provided works perfectly with the exception of when the
$element1 begins with the same string to the first space character. If
$element1 = "Chicago Bears" on the first loop through and $element1 =
"Detroit Lions" on the second pass through, everything is fine.
However, If $element1 = "Chicago Bears" on the first pass through and
$element1 = "Chicago Anything", then that second $element1 does not get
written to the flat file.
 
P

Paul Lalli

(e-mail address removed) then revealed:
Actually, that was a typo on my part. In my actual code, I have $k=1;

So you read the guidelines, but decided it wasn't worth your time to
actually follow them? You've now wasted several people's time, and are
no closer to finding your solution. Good bye.

Paul Lalli
 
M

mbliven1

(e-mail address removed) then revealed:

So you read the guidelines, but decided it wasn't worth your time to
actually follow them? You've now wasted several people's time, and are
no closer to finding your solution. Good bye.

Paul Lalli

I apologize for wasting your time. The work I am doing in perl is on
another system. I have dual monitors with no way to access the
internet from the system the perl is on. I read the part about not
retyping perl code. I had no other choice. It is also strictly
prohibited by policy from transferring data from the non-internet
capable machine to the machine I type this on without supervisor
approval. Believe me, I understand it would have been a whole lot
easier for me to copy and paste.

I am new to this group. I did NOT initially read the policy for
posting until you mentioned it. I tried to correct it and have learned
from it.
 
J

John W. Krahn

My bad about the post. I have now read the guidelines.

Here is the code:

open (FILE, "+<test.txt") || die ("test.txt wont open");

You should include the $! variable in the error message so you know *why* the
file could not be opened.

flock FILE, 2;

You should use the constants from the Fcntl module instead of "magic numbers".

@lines = <FILE>;

$j=0;
$k=0;

foreach $element (@lines) {
($element1, $element2) = split(/\|/, $element);
@splitdata[$j] = $element1;
@splitdata[$k] = $element2;

You are using array slices when you should be using scalars:

Found in /usr/lib/perl5/5.8.6/pod/perlfaq4.pod
What is the difference between $array[1] and @array[1]?

$j = $j + 2;
$k=$k + 2;

Why not just use push:

my @splitdata;
for my $element ( @lines ) {
push @splitdata, split /\|/, $element;
}

And then you don't have to use array subscripts.

Or use map() and you won't need a loop:

my @splitdata = map split( /\|/ ), @lines;


[ snip ]



John
 
B

Ben Morrow

Quoth (e-mail address removed):
My bad about the post. I have now read the guidelines.

....but seem to be unwilling to follow them.
Here is the code:

Where is

use strict;
use warnings;

?
open (FILE, "+<test.txt") || die ("test.txt wont open");

Use two-arg open.
Use lexical filehandles.
Say why the open failed.
The first rule of programming is: never type anything twice. Put the
filename in a variable.

my $database = 'test.txt';
open(my $FILE, '+<', $database) || die("'$database' won't open: $!");

If you use or instead of || those parens are optional:

open my $FILE, '+<', $database
or die "can't open '$database': $!";

but the code's OK like that if you find it easier to understand.
flock FILE, 2;

Don't *ever* use numeric constants for flock, seek, &c. Ask Perl to get
them right for you.
Check that you got the lock.

use Fcntl qw/:flock/; # this goes at the top

flock $FILE, LOCK_EX
or die "can't lock '$database': $!";
@lines = <FILE>;

You need to declare variables under 'use strict'.

my @lines = said:
$j=0;
$k=0;

These would need declaring as well, except you very rarely need loop
counters in Perl. Perl knows how long your arrays are.

You do, however, need

my @splitdata;
foreach $element (@lines) {
^^ my
($element1, $element2) = split(/\|/, $element);

Variables with similar names are a red flag. They mean you should be
using data structures: in this case, an array.

my @elements = split(/\|/, $element);
@splitdata[$j] = $element1;

Don't use @ for accessing single array elements. It appears to work, but
it doesn't mean quite what you think it does. See perldoc -q @array.

$splitdata[$j] = $element1;
@splitdata[$k] = $element2;

This is pointless. $j and $k will always be equal, so you set the same
element twice. Every other element will be blank. I presume you meant to
set successive elements?

Use Perl's array operators, and then you don't need the temporaries
either:

push @splitdata, split /\|/, $element;
$j = $j + 2;

It's generally clearer to use the assignment operators where you can:

$j += 2;

, except, of course, that you don't need this at all.
$k=$k + 2;
}

foreach $p (param()) {

What is param? You didn't include it in your script. I'm going to guess
you meant to put

use CGI qw/:standard/;

at the top: when the posting guidelines say 'a short, complete script'
they mean you need to post the *whole* of your script. You also need to
provide example input: in this case, a query string.

Note that the order of parameters is not guaranteed to be the same as
the order of fields in the form. You should give your paramaters names
and access them by name.
$formvalue = param($p);
@formvalue[$i] = $formvalue;

if ($formvalue eq "") {
@final[$i] = @splitdata[$i];
}
else {
@final[$i] = $formvalue;
}

This can be written more simply as

$final[$i] = $formvalue || $splitdata[$i];

I don't really understand what you are trying to achieve here. A
somewhat cleaner solution might be

my @params = params;
my %final;
$final{$_} = shift @splitdata for @params;
$final{$_} = param($_) for grep param($_), @params;

but I really think that a better answer would be to put the form field
names into your database. Do you have to use that file format? If you
could change to something like GDBM, then you could reduce the whole
script down to

use warnings;
use strict;

use GDBM_File;
use CGI qw/:standard/;

tie my %data, GDBM_File => './data', GDBM_WRCREAT;

$data{$_} = param($_) for grep param($_), params;

as GDBM handles locking for you. This will consider a parameter value of
'0' to be equivalent to '', so you may prefer

$data{$_} = param($_) for grep { length param($_) }, params;

It also assumes that your fields all have unique names; again, this may
not be the case, but you didn't provide us with any input data.
$x=0;
$y=0;
$z=0;

foreach (@final) {
$firstpart = @final[$x];
$secondpart = @final[$y];

Again, $x and $y are the same, so you're getting the same element twice.
Also, the loop loops once for every element in @final, but you're adding
2 to $x each time, so before long you'll be off the end of the array.
$writevalue = "$firstvalue|$secondpart";

What is $firstvalue? Is this your real script, or is that the bug you
were having trouble with? I can't see how it would produce the effect
you describe, though, unless this is not your whole script.
@finalwrite[$z] = $writevalue;
$x = $x + 2;
$y = $y + 2;
$z++;
}

seek FILE, 0, 0;

Don't use magic numbers, use constants.

use Fcntl qw/:seek/;

seek $FILE, 0, SEEK_SET;
truncate FILE, 0;

print FILE @finalwrite;

Since you haven't set $,, this will print the array elements all on one
line, separated by spaces. I don't think that's what you meant.

Ben
 
J

John W. Krahn

Ben said:
Since you haven't set $,, this will print the array elements all on one
line, separated by spaces. I don't think that's what you meant.

The default value for $, is not a space character, you are probably thinking
of "@finalwrite" where the default value of $" is ' '.



John
 

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
473,770
Messages
2,569,583
Members
45,075
Latest member
MakersCBDBloodSupport

Latest Threads

Top