开发者

Regular Expression match and replace

I am writing an Ant script that removes c开发者_如何转开发ontent from a properties file.

Say the properties file is called abc.properties and one of the properties is as such:

this.property=monkey,banana,peanuts,ate,tiger,what,the,heck

My script has to be able to remove one of the items without disturbing the order. Right now, I have this:

<replaceregexp  file="@{targetDir}/setup/properties/abc.properties"
match="this.property(.*),tiger(.*)"
replace="this.property\1\2"/>

Currently, the regular expression works for anything except monkey. I could replace tiger with what, peanuts, or anything except monkey because all of them have a comma in front of them. If I removed the comma from my match expression, there would be an extra comma. The question is, how would I go about writing the regular expression that would replace any item, including monkey?

Thanks


Edited 10-July-2011 to incorporate excellent suggestions of inTide:

How about

match="this.property(.*[=,])tiger(?:,|$)(.*)"
replace="\1\2"

This will leave a bogus terminating comma for "heck" - the last choice. Remove this is a second pass:

match="(this.property.*?)(?:,$|$)"
replace="\1"


Would this work?

<!-- Handle the case when tiger is first -->
<replaceregexp  file="@{targetDir}/setup/properties/abc.properties"
match="this.property=tiger,(.*)"
replace="this.property=\1"/>

<!-- Handle all other cases -->
<replaceregexp  file="@{targetDir}/setup/properties/abc.properties"
match="this.property=(.*),tiger(.*)"
replace="this.property=\1\2"/>

It's a kludge, I admit (and you'd need a third case to handle when tiger is the only item in the list), but it might do the job.


i tried several regexp, but didn't get one for all, the closest was :

 <replaceregexp  file="props.txt"
  match="(this.property=[,?\w+,?]*)monkey,(.*)"
  replace="\1\2"
 />

note the trailing ',' after monkey

case first item = monkey, you have to use :

match="(this.property=[,?\w+,?]*)monkey,(.*)"<br>

to get rid of the ',' otherwise with :

match="(this.property=[,?\w+,?]*)monkey(.*)"

you would get

 this.property=,banana,peanuts,ape,tiger,what,the,heck

using a trailing ',' after the item that shold be deleted would work for all other items, f.e. banana too, but not
for the last item.

case last item = heck :

match="(this.property=[,?\w+,?]*)heck,(.*)"

wouldn't do anything

match="(this.property=[,?\w+,?]*)heck(.*)"

would get :

this.property=monkey,banana,peanuts,ape,tiger,what,the,

only :

match="(this.property=[,?\w+,?]*),heck"

would work.

So, no generic solution, because of problems with the ','

Because of that one has to use more than one step, to make it work for all cases.
So, either use multiple replaceregexp parts or do it the other way around, means load your propertyfile and then edit the property directly - propertyfile stays the same.
As properties in ant are immutable by design, you have to use some Ant Addon or a scripting language with access to the ant api to achieve this.

Following a macrodef working for all cases, which makes use of Flaka :

<project xmlns:fl="antlib:it.haefelinger.flaka">

 <property name="this.property" value="monkey,banana,peanuts,ape,tiger,what,the,heck"/>

 <macrodef name="editcsvproperty">
  <attribute name="csvproperty"/>
  <attribute name="newproperty"/>
  <attribute name="delete"/>
   <sequential>
    <fl:when test="'@{csvproperty}' eq '@{newproperty}'">
     <echo>Overriding existing property => '@{csvproperty}' !!</echo>
    </fl:when>
    <fl:let>
     ; first delete item itself
     @{newproperty} ::= replace(property['@{csvproperty}'], '', '@{delete}')
     ; take care of doubled ,,
     @{newproperty} ::= replace(property['@{csvproperty}'], ',', ',,')
     ; take care of a leading ,
     @{newproperty} ::= replace(property['@{csvproperty}'], '$1', '^,(.+)')
     ; take care of a trailing ,
     @{newproperty} ::= replace(property['@{csvproperty}'], '$1', '(.+),$')
    </fl:let>
   </sequential>
 </macrodef>


 <editcsvproperty
    csvproperty="this.property"
    newproperty="this.property"
    delete="heck"
 />

 <echo>$${this.property} => ${this.property}</echo>
</project>


Try using this:

match="(this.property=(?:.*,)?)tiger(?:,|$)" replace="\1"

Update: To remove just one of the possible surrounding commas takes a bit more work. If the regex lib you are using in Ant supported conditionals you could solve it with help of something like (?(2),). But since you are probably using Javas default Regex lib, you'd have to use alternation, something like:

match="(this.property=(?:[^,]*(?:,[^,]*)*)?)(?:,tiger(?=,|$)|(?<==)tiger(?:,|$))" replace="\1"

Perl test:

my @strings = (
    'this.property=monkey,banana,peanuts,ate,tiger,what,the,heck',
    'this.property=monkey,banana,peanuts,ate,tiger',
    'this.property=tiger,what,the,heck',
    'this.property=tiger',
    );


my $re = qr/(this.property=(?:[^,]*(?:,[^,]*)*)?)(?:,tiger(?=,|$)|(?<==)tiger(?:,|$))/;

for(@strings){
    print ">> \"$_\"\n";
    s/$re/$1/g;
    print "   \"$_\"\n";
}

Output:

>> "this.property=monkey,banana,peanuts,ate,tiger,what,the,heck"
   "this.property=monkey,banana,peanuts,ate,what,the,heck"
>> "this.property=monkey,banana,peanuts,ate,tiger"
   "this.property=monkey,banana,peanuts,ate"
>> "this.property=tiger,what,the,heck"
   "this.property=what,the,heck"
>> "this.property=tiger"
   "this.property="


This is not as hard as it seems. The following works for all cases except where the keyword is the only one. (And for that case, just add another rule.)

<replaceregexp  file="@{targetDir}/setup/properties/abc.properties"
match="this\.property=(.*)(?:,tiger\b|\btiger,)(.*)"
replace="this.property\1\2"/>


How about something like

<replaceregexp
    file="@{targetDir}/setup/properties/abc.properties"
    match="(this.property.*)(?:@{toRemove},|,@{toRemove})"
    replace="\1"
/>


In Python, I would do:

import re

ch = 'this.property=monkey,banana,tigerfish,peanuts,ate,tiger,what,the,heck'
print ch
print '-----------------------------------------------------------'

for word in ('tiger','heck','monkey'):
    regx = re.compile('(?<=this\.property=)(.*?)(,)?%s(?(2)(?=(?:,|$))|,)(.*)' % word, re.M)
    print 'word=='+word
    print regx.sub('\\1\\3',ch)
    print


print
ch = 'this.property=monkey'
print ch
print '-----------------------------------------------------------'
regx = re.compile('(?<=this\.property=)(.*?)(,)?%s(?(2)(?=(?:,|$))|(?:,|$))(.*)' % 'monkey', re.M)
print 'monkey'
print regx.sub('\\1\\3',ch)

result

this.property=monkey,banana,tigerfish,peanuts,ate,tiger,what,the,heck
-----------------------------------------------------------
word==tiger
this.property=monkey,banana,tigerfish,peanuts,ate,what,the,heck

word==heck
this.property=monkey,banana,tigerfish,peanuts,ate,tiger,what,the

word==monkey
this.property=banana,tigerfish,peanuts,ate,tiger,what,the,heck


this.property=monkey
-----------------------------------------------------------
monkey
this.property=


Adding a custom ant task that directly works with property file for this will be more rugged. (ant can call java program, so that could be another option). Since ant allows to use java programs inside javascript(rhino), I tried below code.. you may able to tweak it for your need..

<project name="blah" default="editprops" basedir=".">
    <target name="editprops" >
        <property file="try.props"/>

        <script language="javascript"> <![CDATA[
              importClass(java.util.Properties);
              importClass(java.io.FileReader);
              importClass(java.io.FileWriter);
              importClass(java.io.IOException);
              var properties = new Properties();
              var reader = new FileReader("try.prop");
              properties.load(reader);
            var tt = properties.getProperty("this.property");
            var ll= tt.split ( ",");
            var newval="";
            var i=0;
            for ( i =0; i< ll.length ; i++ ) {
                if ( ll[i] == "monkey" ) {
                    continue;
                }
                if ( newval=="" ) {
                    newval =  ll[i];
                }
                else {
                    newval = newval + "," + ll[i];
                }


            }
            print ( newval);
            properties.setProperty("this.property", newval);
            var writer = new FileWriter ( "try.new");
            properties.store( writer, "today" );

            ]]></script>
    </target>
</project>
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜