CGI/Perl Guide | Learning Center | Forums | Advertise | Login
Site Search: in

  Main Index MAIN
INDEX
Search Posts SEARCH
POSTS
Who's Online WHO'S
ONLINE
Log in LOG
IN

Home: Perl Programming Help: Intermediate:
integer problems

 



Ghigman3
Deleted

Dec 16, 2000, 9:57 AM

Post #1 of 5 (330 views)
integer problems Can't Post

Ok I have a log file with the urls of some of my files and following each url is the number of times it has been visited. my problem is when I try and increment the number by 1 it only adds 1 to the right of the number. here is the code that should increment the number:


Code
if ( 
$query eq "index" || $query eq "portfolio" ||
$query eq "services" || $query eq "tools" || $query eq "contact"
) {
open(DATA, "+< log.dat") || die;
@data=<DATA>;
foreach $line (@data){
if ($line =~ /$query/){
($url,$count)=split(/::/, $line);
$count++;
}
if ($line eq @data[0]) { seek(DATA,0,0) }
elsif ($line eq @data[1]) { seek(DATA,0,1) }
elsif ($line eq @data[2]) { seek(DATA,0,2) }
elsif ($line eq @data[3]) { seek(DATA,0,3) }
elsif ($line eq @data[4]) { seek(DATA,0,4) }
print DATA "$url::$count";
}
}



(This post was edited by japhy on Dec 16, 2000, 9:30 AM)


japhy
Enthusiast

Dec 16, 2000, 10:44 AM

Post #2 of 5 (328 views)
Re: integer problems [In reply to] Can't Post

In the future, please use the &#91;pre&#93; and &#91;/pre&#93; markup tags around your code samples. I have edited your post to make it more readable.

Your main problem is that you're using seek() function incorrectly (please read perldoc -f seek for the full documentation). It doesn't seek by way of line numbers, and the third argument can only be 0, 1, or 2.

You also want to be sure you're using $array[$index], not @array[$index] -- if you run perl with the -w switch, you'll be warned about the misuse of arrays.

What you probably want to do is use a variant of the code I produced in the FAQ section of the forums -- modifying lines of the file.


Code
# $VISITED is the URL whose count is being incremented 
{
local ($^I, @ARGV) = (".bak", "log.dat");
while (<>) {
chomp;
my ($url, $count) = split /::/;
$count++ if $url eq $VISITED;
print join("::", $url, $count), "\n";
}
}

I would also strongly recommend against using "::" as a data separator in your file, since you'll run into problems if you try doing something like:


Code
$name = "jeff"; 
$username = "japhy";
print "$name::ADMIN::$username\n";

To Perl, $name::ADMIN is the variable $ADMIN in the namespace name::. This can be a problem for you.

For more on in-place editing, please read the post in the FAQ section of this forum entitled "Inserting Lines Into Files". This forum is at: Perl Programming Help >> Frequently Asked Questions.

Jeff "japhy" Pinyan -- accomplished hacker, teacher, lecturer, and author


rGeoffrey
User / Moderator

Dec 16, 2000, 10:50 AM

Post #3 of 5 (328 views)
Re: integer problems [In reply to] Can't Post

While it does not address your question, here is a new block of code...


Code
my %types = map { $_ => 1 } qw (index portfolio services tools contact); 
my $rownumber;

if (exists $types{$query}) {
open(DATA, "+< log.dat") || die;
@data=<DATA>;
foreach $line (@data){
if ($line =~ /$query/){
($url,$count)=split(/::/, $line);
$count++;
}

seek(DATA,0,$rownumber);
print DATA "$url::$count";
$rownumber++;
}
}

I introduce two new variables.

%types lets us swap out the 4 elsifs for a single exists in the hash. While it probably will not save much time for just 4 elsifs, this works much better for very long lists of elsifs. And if you ask the question often in the same program, then you can pay the price of building the hash just once duing startup, and then use it again and again for free.

When it came time to print you were asking what line you were on and then printing in the right place. But we already know what line we are on because we know how many times we have entered the foreach loop. So with $rownumber we remember that and completely eliminate the second set of 4 elsifs.

But this will print the first 5 lines of the file everytime you run this program. Is that what you really want or did you only want to do one of them? If that is the case would this be better?


Code
my %types = map { $_ => 1 } qw (index portfolio services tools contact); 
my $rownumber;

if (exists $types{$query}) {
open(DATA, "+< log.dat") || die;
@data=<DATA>;
foreach $line (@data){
if ($line =~ /$query/){
($url,$count)=split(/::/, $line);
$count++;
seek(DATA,0,$rownumber);
print DATA "$url::$count";
}
$rownumber++;
}
}

But now back to your actual problem. What do you mean by " it only adds 1 to the right of the number"? Is the data file just 5 lines, or are you only playing with the first 5 of a much longer file?

Another problem that this scheme might run into is that you are writing over bytes that are already there, and if
the count goes from 99 to 100, you need an extra byte that is probably a "\n" which will mess everything up.



japhy
Enthusiast

Dec 16, 2000, 11:10 AM

Post #4 of 5 (327 views)
Re: integer problems [In reply to] Can't Post

There is still the problem of improper use of seek(). Paraphrasing the documentation, here's how to use the function.

seek(FH, OFFSET, WHENCE)

FH is a filehandle

OFFSET is a number of bytes (it can be negative), relative to WHENCE

WHENCE is either 0 (for start of file), 1 (for current offset), or 2 (for end of file)

Thus, to seek to the end of the file, one would do seek(FH, 0, 2). To go back one byte from where you are now, you would do seek(FH, -1, 1).

You must realize that line numbers are meaningless pieces of data to anything but a human. "Line" number is specifically misleading -- it is actually a "record" number to the computer. Records are, by default, streams of characters that end in a newline. And because records needn't be of the same length, there's no way to jump to a specific record in constant time.

If someone would create an OS where files where kept as linked lists of strings, then things would be far easier. Jumping to a specific record would still take O(n) time, but insertion and deletion and modification would be far more efficient.

Jeff "japhy" Pinyan -- accomplished hacker, teacher, lecturer, and author


rGeoffrey
User / Moderator

Dec 16, 2000, 11:49 AM

Post #5 of 5 (323 views)
Re: integer problems [In reply to] Can't Post

Actually I was making no comment on 'seek', a function that I don't use. And even worse I was editing my post while you were sneaking in and posting yours.

But if I was doing this I would use something that looks more like this...


Code
my %types = map { $_ => 1 } qw (index portfolio services tools contact); 
my $rownumber;

if (exists $types{$query}) {
my $file = 'log.dat';
open(DATA, $file) || die;
@data=<DATA>;
close DATA;

open(DATA, ">$file") || die;
foreach $line (@data){
chomp;
if ($line =~ /$query/){
($url,$count)=split(/::/, $line);
$count++;
print DATA "$url::$count\n";
} else {
print DATA "$line\n";
}
$rowcount++;
last if ($rowcount > 4);
}
splice (@data, 0, 5);
print DATA @data;
close DATA
}

I am assuming that the log file may have more than 5 lines, but not so many more that it is too expensive to just slurp them in and print them back out.


 
 


Search for (options) Powered by Gossamer Forum v.1.2.0

Web Applications & Managed Hosting Powered by Gossamer Threads
Visit our Mailing List Archives