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>
精彩评论