Anonymous Monk has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks, I have an input file which contains a student information as follow:
stdNum stdNam stdLoc stdAge stdGrd 43 gorge Brazil 28 A 12 John FL 83 B+ 66 Sam Canada 43 C
What I need to do is to be able to update this file base on the user information let's say I want to delete student so I should be able to delete the whole line that contains the infromation about the student or if I want to only update somthing let's say the age. I am trying to do it by reading the file making the change and copying to another but don't seem to work for me . I am sure there is a better way to update the file than what I am doing .. Can someoen advice? here is an example of what I am doing :
my $stdInfo="/home/stdInfo"; print " Enter the number of student that you want to delete\n"; my $std = <STDIN>; open(FILE, "$stdInfo") || die "couldn't open $stdInfo\n"; open(FILECOPY, ">std") || die "couldn't open std\n"; while(<FILE>) { chomp; next if ( $_ =~ /^#/ ); if ($_ =~ /^$std/) {s/$_//;} print FILECOPY; } system ( "mv std $stdInfo" ); close(FILECOPY); close(FILE);
give me a funki output and doesn't update the file. thanks

Replies are listed 'Best First'.
Re: updating Input file
by waswas-fng (Curate) on Feb 23, 2004 at 20:58 UTC
    use strict; my $stdInfo="stdInfo"; print " Enter the number of student that you want to delete\n"; my $std = <STDIN>; chomp $std; #open file and read it into an array... open(FILE, "$stdInfo") || die "couldn't open $stdInfo\n"; my @filedata = <FILE>; close (FILE); #open file for writing... open(FILE, ">$stdInfo") || die "couldn't open $stdInfo\n"; foreach my $line (@filedata) { print FILE $line unless ($line =~ /^$std/); } close (FILE);
    If you are doing anything like this to a large file consider using a database or tieed file.


    -Waswas
Re: updating Input file
by pzbagel (Chaplain) on Feb 23, 2004 at 21:02 UTC

    How big is the file you need to work with? Can you read the whole thing into memory or could it possibly get too big to do that. Provided the file won't grow too large, you can do something like:

    my $stdInfo="/home/stdInfo"; print " Enter the number of student that you want to delete\n"; my $std = <STDIN>; #Don't forget to get rid of the newline character from the input chomp $std; #Open the file and read it in: open(FILE, "$stdInfo") || die "couldn't open $stdInfo for reading"; #Each line is stored in an array my @in=<FILE>; close(FILE); #Now open the same file again for output open (FILE, ">$stdInfo") ||die "couldn't open $stdInfo for writing"; #Now print out everything except what you want to delete: for (@in) { print FILE unless ( $_ =~ /^$std/ ); } close(FILE);

    The code is simple and could definitely use a ton of error-checking stuff to make sure the user is input correct input. But I'll leave that up to you.

    Later

      my file contains about 250 line not more than that . Do I need to consider DB or tie with this?
Re: updating Input file
by ambrus (Abbot) on Feb 23, 2004 at 21:09 UTC

    For such a small file, a simple text solution should be enough, no need to use databases. However take care not to lose data because of an error in the script.

    Let me give an example code. This code deletes gorge and changes John's age to two less.

    perl -i~ -we '$_=<>; @f= split; print; while (<>) { @a{@f}= split or w +arn next; $a{stdNam} eq "John" and $a{stdAge}-= 2; $a{stdNam} eq "gor +ge" and next; print join ("\t", @a{@f}), $/; }' file

    Update: as an alternative, just open the file in your editor, and filter the whole text through the same perl command just without the -i~ switch and the filename. I use perl one-liners a lot of times this way. This is convenient as you can immediately see the result, and undo the changes if the script does not work at first. (In vi, you just press gg!G and type the command, starting with perl. In emacs, use ^[<^[>^U^[|. In joe, ^KB^KK^K/.)

      You need to be careful not to delete the wrong (multiple) students too. For the examples that waswas-fng and pzbagel gave, if you attempt to delete student 12 (John), it will also delete users 120-129. . . You might want to consider using a hash with the student number as the key instead of the arrays to prevent that.

      - - arden.

        Noone yet has answered what the problem is witheyour original script you gave.

        The problem is

        1. it chops newlines but does not write them back. Change print FILECOPY; to print FILECOPY $_,$/;.
        2. You did not chomp $std. This is neccessary as otherwise the regexp wont match.
        3. It would be wise to change s/$_// to next.
        4. Also the filename "/home/stdInfo" may be wrong.

        5. Also arden says:

          For the examples that waswas-fng and pzbagel gave, if you attempt to delete student 12 (John), it will also delete users 120-129. . .

          That is also true for the original script in the question. So change /^$std/ to /^\Q$std\E\b/.

        Apart from these your script is correct.

        I belive that my solution is correct in this sense. $a{stdNum}==12 will match only student number 12.

        You only have to make sure that there are no spaces in the stdNam field or change split to split /\s\s+/.

        (Maybe you wanted to post this as a reply to the original question not my post. I'm not sure.)

Re: updating Input file
by ambrus (Abbot) on Feb 23, 2004 at 20:50 UTC

    For a simple solution, read how the -i switch works in perldoc perlrun.

Re: updating Input file
by TomDLux (Vicar) on Feb 24, 2004 at 04:11 UTC

    The Canadian media are full of this crisis, and I'm sure the candidates for the leadership of the UnProgressive Conservative Party will discuss it in a future debate!

    Why! Oh Why! are Canadian students earning such low grades!

    update: I wonder whether it's the Canadians who consider this unfunny enough to downvote, or the non-Canadians?

    --
    TTTATCGGTCGTTATATAGATGTTTGCA