Non ero abbastanza soddisfatto con nessuna delle soluzioni qui. Volevo una soluzione senza stato. E non volevo finire in un ciclo infinito se la mia stringa sostitutiva corrispondeva al modello. Mentre ero a esso ho aggiunto il supporto per un parametro limit
e un parametro restituito count
.(Ho usato un AtomicInteger
per simulare il passaggio di un intero per riferimento.) Ho spostato il parametro callback
alla fine dell'elenco dei parametri, per semplificare la definizione di una classe anonima.
Ecco un esempio di utilizzo:
final Map<String,String> props = new HashMap<String,String>();
props.put("MY_NAME", "Kip");
props.put("DEPT", "R&D");
props.put("BOSS", "Dave");
String subjectString = "Hi my name is ${MY_NAME} and I work in ${DEPT} for ${BOSS}";
String sRegex = "\\$\\{([A-Za-z0-9_]+)\\}";
String replacement = ReplaceCallback.replace(sRegex, subjectString, new ReplaceCallback.Callback() {
public String matchFound(MatchResult match) {
String group1 = match.group(1);
if(group1 != null && props.containsKey(group1))
return props.get(group1);
return match.group();
}
});
System.out.println("replacement: " + replacement);
Ed ecco la mia versione di classe ReplaceCallback:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.*;
public class ReplaceCallback
{
public static interface Callback {
/**
* This function is called when a match is made. The string which was matched
* can be obtained via match.group(), and the individual groupings via
* match.group(n).
*/
public String matchFound(MatchResult match);
}
/**
* Replaces with callback, with no limit to the number of replacements.
* Probably what you want most of the time.
*/
public static String replace(String pattern, String subject, Callback callback)
{
return replace(pattern, subject, -1, null, callback);
}
public static String replace(String pattern, String subject, int limit, Callback callback)
{
return replace(pattern, subject, limit, null, callback);
}
/**
* @param regex The regular expression pattern to search on.
* @param subject The string to be replaced.
* @param limit The maximum number of replacements to make. A negative value
* indicates replace all.
* @param count If this is not null, it will be set to the number of
* replacements made.
* @param callback Callback function
*/
public static String replace(String regex, String subject, int limit,
AtomicInteger count, Callback callback)
{
StringBuffer sb = new StringBuffer();
Matcher matcher = Pattern.compile(regex).matcher(subject);
int i;
for(i = 0; (limit < 0 || i < limit) && matcher.find(); i++)
{
String replacement = callback.matchFound(matcher.toMatchResult());
replacement = Matcher.quoteReplacement(replacement); //probably what you want...
matcher.appendReplacement(sb, replacement);
}
matcher.appendTail(sb);
if(count != null)
count.set(i);
return sb.toString();
}
}
Io in realtà come la vostra risposta originale meglio con la coda la stringa e gli indici restituita. Quindi applicandole al contrario. In questo modo è più semplice, ma sembra fare più lavoro, dovendo ripetere la scansione dell'intera stringa per ogni partita. Grazie per il suggerimento! – Mike
Ho aggiunto di nuovo il suggerimento originale. Le dimensioni di input previste farebbero la differenza se la scansione o la messa in coda e la sostituzione sarebbero state più efficaci. Suppongo che si possa anche avere il metodo di sostituzione in coda, insieme alla stringa di sostituzione ... – jdmichal
Errr ... Misspoke. Ovviamente l'accodamento è sempre più efficace in termini di tempo della CPU. La differenza sarebbe se si tratta di un problema abbastanza grande di cui preoccuparsi. – jdmichal