我认为最好的解决方案是使用正则表达式查找所需的引用字符串,然后替换正则表达式匹配项中的空格。像这样的:
import java.util.regex.*;
class SOReplaceSpacesInQuotes {
public static void main(String[] args) {
Pattern findQuotes = Pattern.compile("\"[^\"]+\\*\"");
for (String arg : args) {
Matcher m = findQuotes.matcher(arg);
StringBuffer result = new StringBuffer();
while (m.find())
m.appendReplacement(result, m.group().replace(" ", "\\\\ "));
m.appendTail(result);
System.out.println(arg + " -> " + result.toString());
}
}
}
运行java SOReplaceSpacesInQuotes 'word1 AND "word2 word3 word4*" OR "word5 word6*" OR word7',然后愉快地产生了输出word1 AND "word2 word3 word4*" OR "word5 word6*" OR word7 -> word1 AND "word2\ word3\ word4*" OR "word5\ word6*" OR word7,这正是你想要的。
模式是"[^"]+\*",但对于Java,反斜杠和引号必须转义。这匹配文字引号、任意数量的非引号、* 和引号,这就是您想要的。这假设 (a) 您不允许嵌入 \" 转义序列,并且 (b) * 是唯一的通配符。如果您嵌入了转义序列,则使用"([^\\"]|\\.)\*"(Java 转义为\"([^\\\\\\"]|\\\\.)\\*\");如果您有多个通配符,请使用"[^"]+[*+]";如果两者都有,请以明显的方式将它们结合起来。处理多个通配符只需让它们中的任何一个在字符串的末尾匹配即可;处理转义序列是通过匹配一个引号后跟任意数量的非反斜杠、非引号字符,或一个反斜杠来完成的。
现在,该模式会找到您想要的带引号的字符串。对于程序的每个参数,然后我们匹配所有参数,并使用m.group().replace(" ", "\\\\ "),用反斜杠和空格替换匹配项(带引号的字符串)中的每个空格。 (这个字符串是\\——为什么需要两个真正的反斜杠,我不确定。)如果你以前没有见过appendReplacement和appendTail(我没有),这就是它们的作用:串联,它们遍历整个字符串,替换与appendReplacement 的第二个参数匹配的任何内容,并将其全部附加到给定的StringBuffer。 appendTail 调用对于捕获最后不匹配的内容是必要的。 documentation for Matcher.appendReplacement(StringBuffer,String) 包含一个很好的使用示例。
编辑:正如 Roland Illig 指出的那样,如果出现某些类型的无效输入,例如 a AND "b" AND *"c",就会变成 a AND "b"\ AND\ *"c",这是有问题的。如果这是一个危险(或者如果它可能在未来成为一个危险,它可能会),那么你应该通过 always 匹配引号使其更加健壮,但只有在它们以通配符。只要您的报价始终适当配对,这将起作用,这是一个弱得多的假设。结果代码非常相似:
import java.util.regex.*;
class SOReplaceSpacesInQuotes {
public static void main(String[] args) {
Pattern findQuotes = Pattern.compile("\"[^\"]+?(\\*)?\"");
for (String arg : args) {
Matcher m = findQuotes.matcher(arg);
StringBuffer result = new StringBuffer();
while (m.find()) {
if (m.group(1) == null)
m.appendReplacement(result, m.group());
else
m.appendReplacement(result, m.group().replace(" ", "\\\\ "));
}
m.appendTail(result);
System.out.println(arg + " -> " + result.toString());
}
}
}
我们将通配符放在一个组中,并使其可选,并让引号的主体不愿意与+?匹配,使其尽可能匹配little并让通配符字符被分组。这样,我们匹配每对连续的引号,并且由于正则表达式引擎不会在匹配中间重新启动,我们只会匹配引号的内部,而不是外部。但是现在我们并不总是想要替换空格——我们只想在有通配符的情况下这样做。这很简单:测试第 1 组是否为null。如果是,则没有通配符,所以用它自己替换字符串。否则,替换空格。事实上,java SOReplaceSpacesInQuotes 'a AND "b d" AND *"c d"' 会产生所需的a AND "b d" AND *"c d" -> a AND "b d" AND *"c d",而java SOReplaceSpacesInQuotes 'a AND "b d" AND "c d*"' 会执行替换以得到a AND "b d" AND *"c d" -> a AND "b d" AND "c\ *d"。