Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations Mike Lewis on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Flat File - Delete Line 6

Status
Not open for further replies.

Extension

Programmer
Nov 3, 2004
311
0
0
CA
Hi,

I'm trying to find a easy way to delete a line based on an ID number. My flat file is in the following format (example)

Code:
test.txt
ID|Name|City|State
10|Bob|Carson|CA
12|Bobby|LongBeach|CA

I'm using the following code to delete a specific line

Code:
	sub deleteUser {
		my ($file, $dl_user) = @_;
		local $^I = "";
		local @ARGV = ($file);
			while (<>) {
				my ($user) = split /:/;
				print unless $user eq $dl_user;
          	}
	}
	
		
	$file = "test.txt";
	$user = "10";
		
	&deleteUser($file,$user);

But I don't want to store a backup in order to achieve this. Is there any other way to it ? Also, to update a line (only certain fields of the line) is there an easy way also ?

Thank you in advance for your help
 
Look into Tie::File
--Paul

Spend an hour a week on CPAN, helps cure all known programming ailments ;-)
 
I like Tie::File too, but you can use the inplace editor and just delete the backup file when you finished. Note that your code has an error that would prevent it from working anyway. Your file is "|" delimited but you split on ":", maybe just a typo.

Code:
my $file = "test.txt";
my $user = "10";
&deleteUser($file,$user);


sub deleteUser {
   my ($file, $dl_user) = @_;
   local $^I = ".bak";
   local @ARGV = ($file);
   while (<>) {
      my ($user) = split(/\|/);
      print unless $user eq $dl_user;
   }
   unlink("$file.bak");
}
 
I always thought the inplace was only available on the command line, star for that

--Paul

Spend an hour a week on CPAN, helps cure all known programming ailments ;-)
 
Thanks Paul.

Obviously the inplace editor is not only available from the command line, but I am sure that the code could be reduced to a nice command line one-liner. One of these days I am going to learn (and remember) the command line options!

Something about teaching an old dog new tricks comes to mind though..... [upsidedown]
 
Ishnid, TWB or dunc will no doubt dazzle in short time, sho'nuff, fer shure ;-)

Spend an hour a week on CPAN, helps cure all known programming ailments ;-)
 
The problem with your script is:

1. You are splitting on a ":" (colon)and the text file contains "|" (pipe) as delimiters.

2. You must escape the "|" in the split.

Try this:

-----------------------------------------------------------
#!/usr/bin/perl

sub deleteUser {
my ($file, $dl_user) = @_;
local $^I = "";

local @ARGV = ($file);
while (<>) {
my ($user) = split /\|/;
print unless ($user == $dl_user);
}
}

$file = "test.txt";
$user = "10";

&deleteUser($file,$user);
----------------------------------------------------------
 
I don't know if it's OS dependent, but if you try and run the inplace editor without defining a backup extension it spits out a software error, somwthing like:

Software error:

Can't do inplace edit without backup at path\to\program.pl line nn.
 
Examples of inplace within a script are pretty rare. I haven't actually used it myself so it's interesting to see. Stars all around again.

The inevitable one-liner Paul requested ;)
Code:
perl -ani -F'\|' -e 'print unless ( $F[0] eq q!10! )' data.txt

@Kevin - it is OS dependent. Perl on Windows throws that error. On Linux, it'll happily continue without creating a backup. My one-liner above will need modifying on Win32.
 
Thank you KevinADC and ChuckerBob.
Yes the ":" instead of the "|" was a typo.

I was mainly looking for an alternative but I guess my code is fine and efficient.


Is there an easy way to UPDATE some specific fields based on the ID number ?
 
Is there an easy way to UPDATE some specific fields based on the ID number ?

Yes.

Code:
my $file = "test.txt";
my $id_user = "10";
my $new_city = 'New York';
my $new_state = 'NY';
&modifyUser($file,$user,$update);


sub deleteUser {
   my ($file, $id_user, $new_city, $new_state) = @_;
   local $^I = ".bak";
   local @ARGV = ($file);
   while (<>) {
      chomp;
      my ($id,$name,$city,$state) = split(/\|/);
      print "$id|$name|$new_city|$new_state\n" if $id eq $id_user;
   }
   unlink("$file.bak");
}
 

What if I want to user another solution rather than the inplace editor.
With the same data, I'm trying to achieve the results; delete a record/line.
My current code is simply deleting everything. Any guidance would be appreciated.
Thank you for your help so far...


Code:
test.txt
ID|Name|City|State
10|Bob|Carson|CA
12|Bobby|LongBeach|CA¸


Code:
my $User = "Bobby";
my $file="test.txt";
	
&DeleteUser($User,$file)
	
sub DeleteUser {

	my ($UserID, $file) = @_;
	
	open(TBL,">> $file") || die("Can't open file");
		my $FieldNames = <TBL>;
	
			while( my $record = <TBL> ) {
   			chomp $record;
        
  			my @field=split(/\|/,$record);
									
					print "$FieldNames \n"; # Print Field Names
					print $record unless $field[1] eq $UserID; # Print Lines 
					
			}
									
		close(TBL);
	}
 
really, the inplace editor is very good for this type of task.

Your method has a few problems. If you are not using the inplace editor you have to make sure you refer to the filehandle when printing to the file, in your example that would be: 'TBL'. You are also opening the file in append mode: '>>' which appends new data to the end of the file, good for adding new records but not for editing other lines. The old standby is to slurp the whole file into an array, and then open the existing file for overwriting and use the array to find and edit the line you need then print it all back to the file.

Code:
my $User = "Bobby";
my $file="test.txt";
    
&DeleteUser($User,$file)
    
sub DeleteUser {
    my ($UserID, $file) = @_;
    open(TBL,"$file") || die("Can't open file");
    my @data = <TBL>;#<-- read file into an array
    close (TBL);
    chomp (@data);
    open(TBL,">$file") || die("Can't open file");<#<-- open for overwriting
    for (@data) {
       my @field = split(/\|/);
       next if ($field[1] eq $UserID);#<-- skips this record
       print "$_\n";        
    }
    close(TBL);
}

file slurping is still considered by many to be a bad habit and inefficient, I am more ambivilant about it. Modern computers are so fast and have tons of system memory for the most part, so slurping small and medium size files into an array seems like a worry more of the past, but if the file is hundreds of megabytes you might want to consider other methods.
 
If the file is hundreds of megabytes, it is a database in all but name. I'd be looking at Tie::DBM at the very least and probabaly DBI and mySql. If you're building infrastructure, the sooner you get a database in the better: you'll need it eventually and once you've got it, you'll start porting your old scripts to use it (been there;-).

It's easy to import and export your flat file and the manipulation becomes trivial.

yours,

f

&quot;As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs.&quot;
--Maurice Wilkes
 
fish, solid advice ...

Spend an hour a week on CPAN, helps cure all known programming ailments ;-)
 
I don't know if KevinADC is still looking at this thread, but I have a question about updating a specific row.

Using the inplace editor, this is what Kevin proposed to update a row. (see code below)

However, this will only write the row and won't re-write the other rows and the header containing the field names.

Any guidance would be appreciated.


Code:
my $file = "test.txt";
my $id_user = "10";
my $new_city = 'New York';
my $new_state = 'NY';
&UpdateUser($file,$user,$update);


sub UpdateUser {
   my ($file, $id_user, $new_city, $new_state) = @_;
   local $^I = ".bak";
   local @ARGV = ($file);
   while (<>) {
      chomp;
      my ($id,$name,$city,$state) = split(/\|/);
      print "$id|$name|$new_city|$new_state\n" if $id eq $id_user;
   }
   unlink("$file.bak");
}


Code:
test.txt
ID|Name|City|State
10|Bob|Carson|CA
12|Bobby|LongBeach|CA
 
try like this:
Code:
sub UpdateUser {
   my ($file, $id_user, $new_city, $new_state) = @_;
   local $^I = ".bak";
   local @ARGV = ($file);
   while (<>) {
      chomp;
      my ($id,$name,$city,$state) = split(/\|/);
      if ($id eq $id_user) {
         print "$id|$name|$new_city|$new_state\n";
      }
      else {
         print "$_\n";
      }
   }
   unlink("$file.bak");
}
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top