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

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

Comment on Search for a BLOCK of text and selectively replace
Select or Download Code
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;
    /prakash
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

      dmm

      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 ;)

    -octo-
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?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://153485]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others romping around the Monastery: (18)
As of 2015-07-28 15:32 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    The top three priorities of my open tasks are (in descending order of likelihood to be worked on) ...









    Results (257 votes), past polls