Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer

Search for a BLOCK of text and selectively replace

by Rhodium (Scribe)
on Mar 22, 2002 at 04:20 UTC ( #153485=perlquestion: print w/replies, xml ) Need Help??
Rhodium has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,
I have a file that looks like
; This is a comment starts with semicolon *DESCRIPTION keyword = foo ; keyword2 = bar *END ; *OPERATION blah bla *END
I have read in this file as a whole. Now I want to do a search and replace only on a particular block, namely the DESCRIPTION block. All blocks start with a "*KEYNAME" and end with a "*END". I want to only deal with the *DESCRIPTION block.

So the questions start..

  1. How can I break this into Blocks so I ensure that I will only work on the DESCRIPTION BLOCK, while at the same time ignoring the contents. I think a split would work, but how do I keep the comments where they are.

  2. As I do my search and replaces, I need to be able to say "Didn't find the $search word, I'm adding the variable before the *ENDS section"

So let's talk code..

#!/bin/perl -w # # Slurp in the file open (RUNSET, text.txt} ) || die "Can't access test\n"; my $page = do { local $/, <RUNSET> }; close RUNSET; # Now How do I selectively find the DESCRIPTION block to # modify it. Look out for the comments.. Within the # DESCRIPTION block I could care less about comments # Assuming I have the DESCRIPTION block do a search # What if i Didn't find it, add it at very end before # the *ENDS.
Edit by dws for tag cleanup

Replies are listed 'Best First'.
Re: Search for a BLOCK of text and selectively replace
by PrakashK (Pilgrim) on Mar 22, 2002 at 05:40 UTC
    Use the range operator.
    open (RUNSET, "<text.txt" ) || die "Can't access test\n"; while (<RUNSET>) { if (/^\*DESCRIPTION$/ .. /^\*END$/) { # do any substitutions } } close RUNSET;
Re: Search for a BLOCK of text and selectively replace
by krazken (Scribe) on Mar 22, 2002 at 04:59 UTC
    Just off the top of my head...I would read in the file, and search for the asterisk. When I found it, I would grab the keyword. This would be my hash key, then I would just push the lines onto the hash until I hit my next keyword...
    ######untested code #should be close in theory though #!/usr/bin/perl -w use strict; my $keyword=''; my %file_hash=(); while(<>) { chomp; next if (/^\;/); if ( /\*/) { $keyword=(split /\*/)[1]; } if ($keyword ne "END") { push @{$file_hash{$keyword}},$_; } } foreach my $line (@{$file_hash{'DESCRIPTION'}) { #do what ever you want with each line that falls in # the description block print "$line\n"; }
    This is probably not the most elegant way of doing it, but it should get you going down some path or at least give you some suggestions. cheers! krazken
Re: Search for a BLOCK of text and selectively replace
by Ri-Del (Friar) on Mar 22, 2002 at 05:41 UTC
    I'm definately not an expert, but I've got a similar script that I use to parse a file in the same way. This is the code I wrote to do so, modified to work with your file. I'm sure there is a better way to do this. It puts whatever is between the *DESCRIPTION and the *END into the @description array. You can then loop over @description and do what you wish with it.
    #!/bin/perl -w use strict; open (DATA, "text.txt") || die "Can't access test\n"; #each element in the array is a single line my @data = <DATA>; close(DATA); #vars to manipulate the data my $line=""; my @description = (""); my $found = "false"; #while it is not the end of the file while($line = shift @data){ chomp $line; if($line =~ "DESCRIPTION"){ $line = shift @data; chomp $line; while(!($line =~ "END")){ if(!($line =~ ";")){ push @description, $line; } $line = shift(@data); chomp $line; } print "[",@description, "]\n"; #if there's going to be more descriptions in the same file #then we can clear the array and keep going through the #file until we run out of lines. Otherwise @description #will continue to accumulate strings #@description = (""); } } if($found eq "false"){ #then we didn't find a description #add it here }
    That will at least answer your first question.

      Yours is closest to the way I would do it. My approach would probably be to use a Finite State Machine, but otherwise works the same way.

      Since Rhodium has said s/he wants to do a search and replace, I presume that the rest of the file should be preserved. For example, run this script with 'prog file >out_file':

      #!/usr/local/bin/perl -w my $inblock = 0; while (<>) { if ($inblock ) { if (/^\*END/ ) { $inblock = 0; } else { s/expression_to_find/replace_with/g; # e.g. } } elsif ( /^\*DESCRIPTION ) { $inblock = 1; } print; # print $_ implicitly }

      Nothing fancy, just does the job


      GIVE a man a fish and you feed him for a day
      TEACH him to fish and you feed him for a lifetime
      First off thanks a lot!

      Now, I like this one because it allows me to verify that I am in fact working on the right block. But this presents the problem that I don't know how to deal with.

      FYI - I know that I will only have one DESCRIPTION block but the stuff inside of DESCRIPTION is what may be elsewhere in the file.

      My problem is ok now I have an array @description now If do my search and replaces does this get reflected in @data? I need to play with this..

      And like you said now I need to work the "crap the variable I want to change isn't in there"
Do this using regular expressions (my solution..)
by flocto (Pilgrim) on Mar 22, 2002 at 06:05 UTC
    I think all of the above snippets work, but it's not how I would have done it, so here it is:
    #!/usr/bin/perl -w use strict; $/ = undef; open (FILE, "< input.txt") or die $!; my $file = <FILE>; close FILE; # only standard stuff so far.. # this will hold the key's value my $value; # remove comments.. $file =~ s/;.*$//g; # check for the block we want if ($file =~ m/\*DESCRIPTION.+?\*END/s) { my $block = $&; # look for the keyword within the block if ($block =~ m/^$keyword\s*=\s*(.+)$/) { $value = $1; } else { $value = "Pattern not found, sorry"; } } else { $value = "Block not found.. Please check!"; } print $value . "\n";
    So, now you have enough examples.. Take your pick ;)

Re: Search for a BLOCK of text and selectively replace
by aersoy (Scribe) on Mar 22, 2002 at 18:06 UTC

    This code will parse the whole file into a hash. Then you can manipulate it via the supplied subroutines (add_keyword, del_keyword, get_keyword). You can find examples to their usages to the end of the code.

    #!/usr/bin/perl -w use strict; use Data::Dumper; sub read_config { my $file = shift; # Slurp in the file open (RUNSET, "<$file") or die "Can't access '$file': $!"; my %conf; my $handle; while (<RUNSET>) { chomp; next if $_ eq '' or /^;/; if (/^\*(.*)$/) { if ($1 eq 'END') { undef($handle); } else { $handle = $1; %{$conf{$handle}} = (); } next; } if ($handle) { my($var, $value) = split('\s*=\s*', $_, 2); next unless defined $var; $conf{$handle}{$var} = $value; } } close RUNSET; return \%conf; } sub check_params { my ($config, $block, $keyword, $value) = @_; return unless ref($config) eq 'HASH'; return unless defined($block); return unless ref($config->{$block}) eq 'HASH'; return unless defined($keyword); return @_; } sub add_keyword { my ($config, $block, $keyword, $value) = check_params(@_); return unless defined($config); if (defined(${$config->{$block}}{$keyword})) { print "Replacing value of $keyword in $block (previously " . ${$config->{$block}}{$keyword} . ").\n"; } else { print "Adding the new $keyword keyword to $block block.\n"; } ${$config->{$block}}{$keyword} = $value; } sub del_keyword { my ($config, $block, $keyword) = check_params(@_); return unless defined($config); if (defined(${$config->{$block}}{$keyword})) { print "Deleting $keyword from $block.\n"; return delete(${$config->{$block}}{$keyword}); } else { print "Tried to delete a nonexistant key $keyword in $block block. +\n"; } } sub get_keyword { my ($config, $block, $keyword) = check_params(@_); return unless defined($config); if (defined(${$config->{$block}}{$keyword})) { return ${$config->{$block}}{$keyword}; } else { print "Tried to access a nonexistant key $keyword in $block block. +\n"; } } my $config = read_config('test.txt'); add_keyword($config, 'DESCRIPTION', 'test', 10); add_keyword($config, 'DESCRIPTION', 'keyword', 'aa'); del_keyword($config, 'DESCRIPTION', 'keyword2'); del_keyword($config, 'DESCRIPTION', 'keyword3'); print Dumper($config); exit;

    I hope this helps.

    Alper Ersoy

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://153485]
Approved by root
and all is quiet...

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (5)
As of 2018-05-26 14:03 GMT
Find Nodes?
    Voting Booth?