Big problem with @array and Chomp ... I think :o

R

Robert TV

Hi Everyone,

I'm hoping that you might be able to shed some light on a major problem I am
having. I have been working on a few simple lines of code for over 8 hours
and cannot for the life of me figure it out. I'll start off by explaining
what my intent is for the script to do. The big program I have been writing
sends out emails for our employees. My problem has to do with 2 specific
modules of the program.

a) I have an address book module. This is a Web form with two inputs ... a
name input and an email input. Here I can add entires one at a time to the
address book text file.
b) I have a CSV content module. This is a Web form with a text area input. I
can take the contents of a CSV file, paste it into the textarea, and the
data gets saved to the address book text file.

The first module is working correctly. I use the "|" character to separate
names and emails on a single line. Here is the code I use to add the entry
to the address book text file, and then display it on a web page:

<-- Start Code Example -->

$definedname = CGI::param('name');
$definedemail = CGI::param('email');
$newrecipient = "$definedname|$definedemail\n";

open (ADDRESSES, ">>$activeuser/addressbook.txt") or die "Can't open file:
$!";
flock (ADDRESSES, LOCK_EX) or die "Can't lock file: $!";
print ADDRESSES $newrecipient;
close(ADDRESSES);

<-- End Code Example -->

Notice I use the >> open type to "append" data into the addressbook so that
entries accumulate, and I also specify to add a "\n" to $newrecipient so
that each entry is on it's own line. Below is the code I use to get the data
and display it in a Web page:

<-- Start Code Example -->

print "Content-type: text/html \n\n";
print <<PRINTHTML;
<html>
<head>
<title>Email Communications System</title>
</head>
<body>
<table border="0" cellspacing="0" width="100%" cellpadding="0">
PRINTHTML

open (ADDRESSES, "<$activeuser/addressbook.txt") or die "Can't open file:
$!";
@recipients=<ADDRESSES>;
close(ADDRESSES);

foreach $recipient(@recipients) {
chomp ($recipient);
($name,$email)=split(/\|/,$recipient);

print <<PRINTHTML;
<tr>
<td width="279" class="bodytext" height="19">&nbsp;$name</td>
<td width="279" class="bodytext" height="19">&nbsp;$email</td>
</tr>
PRINTHTML
}
print <<PRINTHTML;
</table>
</body>
</html>
PRINTHTML
exit;

<-- End Code Example -->

This above seems to be working correctly. The code builds a table separating
the names and email addresses. Notice I need to use the chomp ($recipient);
command so that the @array's "\n" gets removed. I talk abut the chomp
because I believe my main problem may be in this general area. Below is the
source code of the 2 entry html generated page (minus start and end HTML):

<-- Start Code Example -->

<table border="0" cellspacing="0" width="100%" cellpadding="0">
<tr>
<td width="279" class="bodytext" height="19">&nbsp;Joe Smith</td>
<td width="279" class="bodytext" height="19">&nbsp;[email protected]</td>
</tr>
<tr>
<td width="279" class="bodytext" height="19">&nbsp;Jnae Smith</td>
<td width="279" class="bodytext" height="19">&nbsp;[email protected]</td>
</tr>
</table>

<-- End Code Example -->

Looks to be correct. So on to the main problem ... As mentioned above I have
another section in my program where I can paste CSV data into a textarea
input. When submitted, it's supposed to completely overwrite and present
data in the address book text file. Here are some snippets from the CSV
module:

<-- Start Code Example -->

@recipients = CGI::param('csvdata');

open (FILE,">>$activeuser/csvdump.txt");
close(FILE);

open (ADDRESSES, ">$activeuser/csvdump.txt") or die "Can't open file: $!";
print ADDRESSES @recipients;
close(ADDRESSES);

open (ADDRESSES, "<$activeuser/csvdump.txt") or die "Can't open file: $!";
@recipients = <ADDRESSES>;
close(ADDRESSES);

unlink <$activeuser/csvdump.txt>;

<-- End Code Example -->

Ok, right about now your wondering why did I take the form contents, write
it to a temp file, then open and get the data back from the temp file. For
some reason I cannot get the code to work any other way. I will explain why
a bit later ... onto the CSV parsing code:

<-- Start Code Example -->

foreach $recipient (@recipients) {
chomp ($recipient);
($name,$email)=split(/,/,$recipient);
$recipient = "$name|$email\n";
}

open (ADDRESSES, ">$activeuser/addressbook.txt") or die "Can't open file:
$!";
print ADDRESSES @recipients;
close(ADDRESSES);

<-- End Code Example -->

Look reasonable right? Start a loop for each element in the @array, split
the data based on a comma, then redefine $recipient as "$name|$email\n"
Notice I replace the comma with the "|" which my recipient display module
seen about uses. I use the "|" character because sometimes people have
comma's in the name etc and that would screw up the script. So ... did the
above work correctly? When I examine the addressbook.txt file, I see all
entires I typed in the textarea, with comma's replaced, and each entry is on
its own line. So I go back to the recipients display module and this is what
is happening to the HTML source code:

<-- Start Code Example -->

<table border="0" cellspacing="0" width="100%" cellpadding="0">
<tr>
<td width="279" class="bodytext" height="19">&nbsp;Joe Smith</td>
<td width="279" class="bodytext" height="19">&nbsp;[email protected]
</td>
</tr>
<tr>
<td width="279" class="bodytext" height="19">&nbsp;Jnae Smith</td>
<td width="279" class="bodytext" height="19">&nbsp;[email protected]
</td>
</tr>
</table>

<-- End Code Example -->

Notice that the </td> tages that are supposed to appear directly to the
right the email address, are being forced down to their own line. This
suggests to me that the chomp is not working correctly for the CSV data and
a secret/hidden "\n" is still pressent in the $email variable. Remember it
worked fine for the module where I enter names and emails in their own input
forms individually. You might think this is a simple cosmetic annoyance but
it is causing havok on other areas of the program. Lets go over the
different recipient entry adding methods ....

a) Manual Module = I add names and emails individually into separate input
fields. (Single line input fields have no "\n"s) I join the two elements
together and add a "\n" so future entries go on their own line
"$definedname|$definedemail\n" I print the single entry to the address book
text file use the "append" operator >>. When viewing the data, the chomp
command that is used works correctly and the generated HTML is sound.

b) CSV Module = Many entires can be added at once by pasting CSV data into
the textarea. The text area data is assigned to an @array and gets written
to a temp file. The temp file is then opened and the data is "re-retrieved"
overwritting the @array's previous assignment. Through a loop, I split each
line by the comma and reassign the loops string to "$name|$email\n" ... same
format as the Manual Module. This is NOT working ... for some reason the
"\n"s are different, the display module with not remove the "\n" from the
end of the email adddress, thus the </td> html tag getting forces to a line
below. IF I do not add a "\n" to the loop in the CSV module, when I print or
view the @array data, everything is joined to a single line and I get
name|emailname|emailname|email etc etc. rather than each entry on it's own
lines as required.

Oh and about the wierd thing I do above ... assign the CSV form data to an
@array, write to a file, re open the file and get data back ... I do this
because the raw data from the textarea form will not for some reason loop
correctly in the foreach. Here are the results from each method:

Method 1 --- using the raw data. The textarea has this CSV data
Jane,[email protected]
Mike,[email protected]
Kris,[email protected]

After the loop, the printed results are:
Jane|[email protected] Mike
-- See names are missing?

Method 2 --- using data read from text file. The textarea has this CSV data
Jane,[email protected]
Mike,[email protected]
Kris,[email protected]

After the loop, the printed results are:
Jane|[email protected]
Mike|[email protected]
Kris|[email protected]
-- Correct layout, but is still wrong somwhow

Have you any input on what's going wrong here? After 8 hours of fighting it
I am almost giving up. I'm sorry this message got so long, I wanted to be
thourough. Thank you for your time..

Robert
 
T

Tad McClellan

Robert TV said:
@recipients=<ADDRESSES>;


You can chomp() them all at once instead of in an explicit loop:

chomp @recipients;

open (FILE,">>$activeuser/csvdump.txt");
close(FILE);


What is the purpose of that code?

unlink <$activeuser/csvdump.txt>;


Is there more than one file involved here?

If not, then why are you glob()ing?

b) CSV Module = Many entires can be added at once by pasting CSV data into
the textarea. The text area data is assigned to an @array and gets written
to a temp file. The temp file is then opened and the data is "re-retrieved"
overwritting the @array's previous assignment.


What is the purpose of doing that?

Oh and about the wierd thing I do above ... assign the CSV form data to an
@array, write to a file, re open the file and get data back ... I do this
because the raw data from the textarea form will not for some reason loop ^^^^^^^^^^^^^^^
correctly in the foreach.


Find out the reason, and the strangeness is likely to disappear...

Here are the results from each method:

Method 1 --- using the raw data. The textarea has this CSV data
Jane,[email protected]
Mike,[email protected]
Kris,[email protected]


How do you know that that is what it has?

Have you print()ed it out in your program?

Have you tried running the program from the command line?

After the loop, the printed results are:
Jane|[email protected]
Mike|[email protected]
Kris|[email protected]
-- Correct layout, but is still wrong somwhow
^^^^^^^^^^^^^^^^^^^

What is it that is "somehow" wrong?

Looks fine to me...

Have you any input on what's going wrong here?


It is most likely related to how you are processing the textarea
data. If you can post a short and complete program that duplicates
your problem, we can probably help you solve it...
 
T

Tore Aursand

$definedname = CGI::param('name');
$definedemail = CGI::param('email');
$newrecipient = "$definedname|$definedemail\n";

Are you using strict and warnings? And what if there's a '|' in the name
and/or email address?
print "Content-type: text/html \n\n";
print <<PRINTHTML;

This can't be repeated too often: Consider using one of the many template
solutions for separating code and presentation. HTML::Template looks like
a popular (and good, of course) choice.
@recipients=<ADDRESSES>;
foreach $recipient(@recipients) {
chomp ($recipient);

Actually, you can chomp all the elements in @recipients in one go;

chomp( @recipients );
open (FILE,">>$activeuser/csvdump.txt");
close(FILE);
Huh?

unlink <$activeuser/csvdump.txt>;

You probably don't want to glob() here. Try this one instead:

unlink "$activeuser/csvdump.txt";
...onto the CSV parsing code:

....which really isn't that good. There are multiple CSV-related modules
on CPAN which does the job much better.
After the loop, the printed results are:
Jane|[email protected]
Mike|[email protected]
Kris|[email protected]
-- Correct layout, but is still wrong somwhow

It is _somehow_ wrong? Somehow how? :)
 
K

Kevin Shay

Robert TV said:
@recipients = CGI::param('csvdata');

open (FILE,">>$activeuser/csvdump.txt");
close(FILE);

open (ADDRESSES, ">$activeuser/csvdump.txt") or die "Can't open file: $!";
print ADDRESSES @recipients;
close(ADDRESSES);

open (ADDRESSES, "<$activeuser/csvdump.txt") or die "Can't open file: $!";
@recipients = <ADDRESSES>;
close(ADDRESSES);

unlink <$activeuser/csvdump.txt>;

<-- End Code Example -->

Ok, right about now your wondering why did I take the form contents, write
it to a temp file, then open and get the data back from the temp file. For
some reason I cannot get the code to work any other way.

The problem may lie in the first line of code above:

@recipients = CGI::param('csvdata');

It sounds like you have a single HTML form field called csvdata, into
which the user enters multiple lines of text. If that's the case, the
CGI module will not automatically turn this into a list. It's a single
value. So the entire set of data is being placed into the first
element of @recipients.

When you print that one-element array to a file, it simply prints that
one element; the element happens to have multiple lines, so the
resulting file will have multiple lines. Then, when you re-open the
file and do this:

@recipients = <ADDRESSES>;

Perl reads the lines of the file into the array, one line per element,
which is what you wanted in the first place. So you could probably
just do this to avoid the whole temp file workaround:

@recipients = split(/\n/, CGI::param('csvdata'));

As for the actual chomp problem, it may have something to do with the
way different operating systems encode newlines differently (\n on
Unix, \r\n on DOS/Windows, \r on Mac). Depending on which platform the
user's browser is running on, you may not be getting just \n
characters separating the lines. So you might want to normalize
everything to \n newlines:

my $csvdata = CGI::param('csvdata');
$csvdata =~ s/\r\n?/\n/g;
@recipients = split(/\n/, $csvdata);

Kevin
 
R

Robert TV

YES YES YES!!!!! Your code examples have fixed my problem. I had no idea
that there were "\r"s AND "\n"s present in each newline, and now that I
replace the "\r\n" with just "\n" the code is working correctly. You are the
best man!

Robert

Kevin Shay said:
Robert TV said:
@recipients = CGI::param('csvdata');

open (FILE,">>$activeuser/csvdump.txt");
close(FILE);

open (ADDRESSES, ">$activeuser/csvdump.txt") or die "Can't open file: $!";
print ADDRESSES @recipients;
close(ADDRESSES);

open (ADDRESSES, "<$activeuser/csvdump.txt") or die "Can't open file: $!";
@recipients = <ADDRESSES>;
close(ADDRESSES);

unlink <$activeuser/csvdump.txt>;

<-- End Code Example -->

Ok, right about now your wondering why did I take the form contents, write
it to a temp file, then open and get the data back from the temp file. For
some reason I cannot get the code to work any other way.

The problem may lie in the first line of code above:

@recipients = CGI::param('csvdata');

It sounds like you have a single HTML form field called csvdata, into
which the user enters multiple lines of text. If that's the case, the
CGI module will not automatically turn this into a list. It's a single
value. So the entire set of data is being placed into the first
element of @recipients.

When you print that one-element array to a file, it simply prints that
one element; the element happens to have multiple lines, so the
resulting file will have multiple lines. Then, when you re-open the
file and do this:

@recipients = <ADDRESSES>;

Perl reads the lines of the file into the array, one line per element,
which is what you wanted in the first place. So you could probably
just do this to avoid the whole temp file workaround:

@recipients = split(/\n/, CGI::param('csvdata'));

As for the actual chomp problem, it may have something to do with the
way different operating systems encode newlines differently (\n on
Unix, \r\n on DOS/Windows, \r on Mac). Depending on which platform the
user's browser is running on, you may not be getting just \n
characters separating the lines. So you might want to normalize
everything to \n newlines:

my $csvdata = CGI::param('csvdata');
$csvdata =~ s/\r\n?/\n/g;
@recipients = split(/\n/, $csvdata);

Kevin
--
perl -MLWP::UserAgent -e '$u=new LWP::UserAgent;$u->agent("japh");
print join(" ",(split(/\s+/,(split/\n/,$u->request(HTTP::Request->
new(GET=>join("",split(/\n/,"http://groups.google.com/groups?selm=
4365%40omepd.UUCP&output=gplain"))))->content)[60]))[0..3]),",\n"'
 
B

Ben Morrow

Robert TV said:
YES YES YES!!!!! Your code examples have fixed my problem. I had no idea
that there were "\r"s AND "\n"s present in each newline, and now that I
replace the "\r\n" with just "\n" the code is working correctly. You are the
best man!

Don't top post.

Use \015 and \012 instead of \r and \n when dealing with data from the
network: they are not necessarily equivalent.

It is best to accept any line ending: run your textarea data through

s{\015|\015?\012}{\n}sg

before using it.

Ben
 

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

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,020
Latest member
GenesisGai

Latest Threads

Top