I think the best you can do with String.replace() is something like:
String string = ...
for (;;)
{
String original = string;
string = string.replaceFirst("(<pre>.*?)a(.*?</pre>)", "$1b$2");
if (original.equals(string))
break;
}
(EDIT: @Bohemian has noted the above regex doesn't work correctly. So it needs to be changed to:
(<pre>(?:(?!</pre>).)*a((?:(?!<pre>).)*</pre>) (untested) to avoid matching outside a <pre>...</pre> section. With this change, we don't need the *? quantifier and can use the more common "greedy" (*) quantifier. This is starting to look a lot like my other answer, which I only really meant as a joke!)
You're better off using a Matcher (following code off the top of my head):
import java.util.regex.Pattern;
import java.util.regex.Matcher;
Pattern pattern = Pattern.compile("(?<=<pre>)(.*?)(?=</pre>)");
Matcher m = pattern.matcher(string);
StringBuffer replacement = new StringBuffer();
while (matcher.find())
{
matcher.appendReplacement(replacement, "");
// Careful using unknown text in appendReplacement as any "$n" will cause problems
replacement.append(matcher.group(1).replace("a", "b"));
}
matcher.appendTail(replacement);
String result = replacement.toString();
Edit: Changed pattern above so that it does not match surrounding <pre> and </pre>.
<pre>(.*?)a(.*?)</pre>->$1b$2but it isn't working</?pre>with ` char so it doesn't look like html anymore.