Replace the first instance only of a string

F

Francois Massion

I couldn't find so far a solution to a quite common problem:

There is a document in which some words occur many times. I want to
replace ONLY THE FIRST instance of the word and leave the other
instances unchanged.

Here my code:
(...)

foreach $sentence (@document) {
chomp $sentence;

$sentence =~ s/Subject/Topic/;

push (@result,$sentence);
}

(...)

I have tried to use the modifier "o" for "once" but it didn't work as
well:

$sentence =~ s/Subject/Topic/o;

Any suggestion?

Thanks

Francois
 
C

ccc31807

I couldn't find so far a solution to a quite common problem:

There is a document in which some words occur many times. I want to
replace ONLY THE FIRST instance of the word and leave the other
instances unchanged.

I don't know what your input document looks like. Ordinarily, I read
in a document line by line, 'line' being defined as a string ending in
a newline, \n. I would guess that you want something like this:

open INFILE, '<', 'inputfile.txt';
open OUTFILE, '>', 'outputfile.txt';
while (<INFILE>)
{
if ($_ =~ /target/)
{
$_ =~ s/target/change/;
last;
}
}
close INFILE;
close OUTFILE;

This is off the cuff, untested, and Sinan will huff 'sloppy', but the
logic is good even if you have to play with the code a bit.

CC
 
A

A. Sinan Unur

I don't know what your input document looks like. Ordinarily, I read
in a document line by line, 'line' being defined as a string ending in
a newline, \n. I would guess that you want something like this:

open INFILE, '<', 'inputfile.txt';
open OUTFILE, '>', 'outputfile.txt';
while (<INFILE>)
{
if ($_ =~ /target/)
{
$_ =~ s/target/change/;
last;
}
}
close INFILE;
close OUTFILE;

This is off the cuff, untested, and Sinan will huff 'sloppy', but the
logic is good even if you have to play with the code a bit.

I will not huff 'sloppy'. It is inane. If you are using $_, use it. The
s/// operator returns a value, use it. As it is, you are doing the same
work twice -- finding if the line contains 'target'.

Also, he probably does not want to abort the loop, but to write the rest
of the document unchanged to the output file after the first
replacement.

#!/usr/bin/perl

use strict;
use warnings;

my $replaced;

while ( <DATA> ) {
$replaced or $replaced = s/words/sentences/;
print;
}

__DATA__
A moth ate words. It seemed to me
a strange event when I heard of that wonder,
that a worm, a thief in darkness, should devour
the songs of men, glorious utterance
and a place of strong being. The thievish visitor
was no whit the wiser for swallowing the words.





--
A. Sinan Unur <[email protected]>
(remove .invalid and reverse each component for email address)

comp.lang.perl.misc guidelines on the WWW:
http://www.rehabitation.com/clpmisc/
 
S

smallpond

__DATA__
    A moth ate words. It seemed to me
    a strange event when I heard of that wonder,
    that a worm, a thief in darkness, should devour
    the songs of men, glorious utterance
    and a place of strong being. The thievish visitor
    was no whit the wiser for swallowing the words.

I'm going to remember that poem when a virus
wipes out my hard drive.
 
F

Francois Massion

(e-mail address removed):









I will not huff 'sloppy'. It is inane. If you are using $_, use it. The
s/// operator returns a value, use it. As it is, you are doing the same
work twice -- finding if the line contains 'target'.

Also, he probably does not want to abort the loop, but to write the rest
of the document unchanged to the output file after the first
replacement.

#!/usr/bin/perl

use strict;
use warnings;

my $replaced;

while ( <DATA> ) {
    $replaced or $replaced = s/words/sentences/;
    print;

}

__DATA__
    A moth ate words. It seemed to me
    a strange event when I heard of that wonder,
    that a worm, a thief in darkness, should devour
    the songs of men, glorious utterance
    and a place of strong being. The thievish visitor
    was no whit the wiser for swallowing the words.

--
A. Sinan Unur <[email protected]>
(remove .invalid and reverse each component for email address)

comp.lang.perl.misc guidelines on the WWW:http://www.rehabitation.com/clpmisc/- Zitierten Text ausblenden -

- Zitierten Text anzeigen -

Hi Sinan,

Thanks. It is exactly what I needed and works fine! I didn't know the
syntax with "$replaced or $replaced". What is the working principle
behind this?

Francois
 
J

Jim Gibson

Francois Massion said:
Thanks. It is exactly what I needed and works fine! I didn't know the
syntax with "$replaced or $replaced". What is the working principle
behind this?

The logic is that the 'or' operator is short-circuited. That is, the
expression 'A or B' will be true if either A or B is true. If A is
true, then B need not be evaluated, and Perl does not even evaluate the
expression B. So in the expression

$replaced or $replaced = s/words/sentences/;

if $replaced is true, the assignment expression $replaced = s/../../;
will not be executed, and no substitution will be done. If $replaced is
false, then the substitution will be attempted and the result (true if
the substitution occurred) stored in $replaced. The effect is that only
the first string in the file will be substituted for.
 
A

A. Sinan Unur

Please do not quote sigs.

....
Thanks. It is exactly what I needed and works fine! I didn't know the
syntax with "$replaced or $replaced". What is the working principle
behind this?

The s/// operator returns the number of substitutions made. In this
case, it can only ever return zero or one.

The or operator does not evaluate the expression on the right if the
first one is true. So,

$replaced or $replaced = s/words/sentences/;

first looks at the value of $replaced. If that is true, then it does not
try the substitution. If it is false, it does try the substitution. If
the substitution fails, $replaced remains false. If it succeeds,
$replaced becomes true so that the substitution will not be attempted
next time through the loop. Once a substition succeeds, $replaced will
remain true for the rest of the loop.

Read perldoc perlop.

Sinan

--
A. Sinan Unur <[email protected]>
(remove .invalid and reverse each component for email address)

comp.lang.perl.misc guidelines on the WWW:
http://www.rehabitation.com/clpmisc/
 
J

Jürgen Exner

Francois Massion said:
while ( <DATA> ) {
    $replaced or $replaced = s/words/sentences/;
[...]
Thanks. It is exactly what I needed and works fine! I didn't know the
syntax with "$replaced or $replaced". What is the working principle
behind this?

You are using the wrong precedence. It is not "$replaced or $replaced"
but
$replaced or ($replaced = s/words/sentences/);

Meaning: if a substitute is succeeding then $replaced will be set to a
true value.
Then in all following iterations $replaced will be true already and the
short-circuit evaluation of 'or' will prevent the second argument of the
boolean expression to be ever evaluated again.

A neat trick, I have to say.

jue
 
W

Willem

J?rgen Exner wrote:
) Then in all following iterations $replaced will be true already and the
) short-circuit evaluation of 'or' will prevent the second argument of the
) boolean expression to be ever evaluated again.

Does ||= also use short-circuiting ?

I.E.:

$replaced ||= s/foo/bar/;


Anyway,

$replaced = s/foo/bar/ unless $replaced;

Seems more descriptive of its purpose.


SaSW, Willem
--
Disclaimer: I am in no way responsible for any of the statements
made in the above text. For all I know I might be
drugged or something..
No I'm not paranoid. You all think I'm paranoid, don't you !
#EOT
 
A

A. Sinan Unur

J?rgen Exner wrote:
) Then in all following iterations $replaced will be true already and
) the short-circuit evaluation of 'or' will prevent the second
) argument of the boolean expression to be ever evaluated again.
....

$replaced = s/foo/bar/ unless $replaced;

Seems more descriptive of its purpose.

Beauty is in the eye of the beholder. In this case, the substitution
happens only once so

$replaced or $replaced = s/foo/bar/;

makes more sense to me because it relegates the operation that is less
frequent to the end of the sentence.

FWIW:

C:\> perl -MO=Deparse,-p -e "$replaced = s/foo/bar/ unless $replaced;"
($replaced or ($replaced = s/foo/bar/));

;-)

--
A. Sinan Unur <[email protected]>
(remove .invalid and reverse each component for email address)

comp.lang.perl.misc guidelines on the WWW:
http://www.rehabitation.com/clpmisc/
 
A

A. Sinan Unur

J?rgen Exner wrote:
) Then in all following iterations $replaced will be true already and
the ) short-circuit evaluation of 'or' will prevent the second
argument of the ) boolean expression to be ever evaluated again.

Does ||= also use short-circuiting ?

Yes it does.
$replaced ||= s/foo/bar/;

Beautiful if a little more cryptic looking ;-)

My brain short circuits to using or to control flow but in this case that
is not needed.

Sinan

--
A. Sinan Unur <[email protected]>
(remove .invalid and reverse each component for email address)

comp.lang.perl.misc guidelines on the WWW:
http://www.rehabitation.com/clpmisc/
 
J

Jürgen Exner

Willem said:
J?rgen Exner wrote:
) Then in all following iterations $replaced will be true already and the
) short-circuit evaluation of 'or' will prevent the second argument of the
) boolean expression to be ever evaluated again.

Does ||= also use short-circuiting ?

I.E.:

$replaced ||= s/foo/bar/;

I am not familiar with this operator.
Anyway,

$replaced = s/foo/bar/ unless $replaced;

Seems more descriptive of its purpose.

TIMTOWTDI.

jue
 
U

Uri Guttman

JE> I am not familiar with this operator.

then you should be. it is very useful for defaulting values or making
sure something is set so it doesn't trigger warnings. it's only failing
is that it tests for boolean truth so it will be false on 0 or ''. 5.10
has the //= op which does the same thing but tests for undef.

here is a common usage for ||=

sub foo {
my( $bar ) = @_ ;

# set bar to a default if nothing was passed in

$bar ||= 'baz' ;

or if '' or 0 are legal values then do this in 5.10:

$bar //= 'baz' ;

uri
 
J

Jim Gibson

Jürgen Exner said:
I am not familiar with this operator.

But you are no doubt familiar with other assignment operators, such as

$a += 2;

being equivalent to

$a = $a + 2;


So in the same way

$a ||= 1;

is equivalent to

$a = $a || 1;
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,969
Messages
2,570,161
Members
46,710
Latest member
bernietqt

Latest Threads

Top