Я думаю, что лучшее решение - использовать регулярное выражение для поиска нужных строк в кавычках, а затем заменить пробелы внутри совпадения регулярного выражения. Примерно так:
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 необходимо экранировать обратные слеши и кавычки. Это соответствует буквенной кавычке, любому количеству не кавычек, *
и кавычке, что вам и нужно. Это предполагает, что (а) вам не разрешено встраивать escape-последовательности \"
, и (б) что *
является единственным подстановочным знаком. Если у вас есть встроенные escape-последовательности, используйте "([^\\"]|\\.)\*"
(для Java это экранировано \"([^\\\\\\"]|\\\\.)\\*\"
); если у вас несколько подстановочных знаков, используйте "[^"]+[*+]"
; и если у вас есть оба, объедините их очевидным способом. Работа с несколькими подстановочными знаками заключается в том, чтобы позволить любому из них совпадать в конце строки; Работа с escape-последовательностями выполняется путем сопоставления кавычки, за которой следует любое количество символов без обратной косой черты, без кавычек, или с обратной косой чертой, предшествующей чему-либо.
Теперь этот шаблон находит нужные вам строки в кавычках. Для каждого аргумента программы мы затем сопоставляем их все и, используя m.group().replace(" ", "\\\\ ")
, заменяем каждый пробел в совпадении (строка в кавычках) на обратную косую черту и пробел. (Эта строка \\
- зачем нужны две настоящие обратные косые черты, я не уверен.) Если вы раньше не видели appendReplacement
и appendTail
(я не видел), вот что они делают: в тандеме они перебирают всю строку, заменяя все, что было найдено, вторым аргументом на appendReplacement
, и добавляют все это к данному StringBuffer
. Вызов appendTail
необходим для перехвата того, что не совпадает в конце. Документация для Matcher.appendReplacement(StringBuffer,String)
содержит хороший пример их использования.
Редактировать: Как указал Роланд Иллиг, это проблематично, если могут появиться определенные виды недопустимых вводов, например a AND "b" AND *"c"
, которые станут a AND "b"\ AND\ *"c"
. Если это опасность (или если она может стать опасной в будущем, что, вероятно, может), то вы должны сделать ее более надежной, всегда сопоставляя кавычки, но заменяя только если они заканчивались на подстановочный знак Это будет работать до тех пор, пока ваши кавычки всегда соотносятся друг с другом, что является более слабым предположением. Полученный код очень похож:
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());
}
}
}
Мы помещаем подстановочный знак в группу и делаем его необязательным, а тело кавычек неохотно набираем +?
, чтобы он соответствовал как маленький , насколько это возможно, и позволяем подстановочному символу получить сгруппированы. Таким образом, мы сопоставляем каждую последующую пару кавычек, и так как механизм регулярных выражений не перезапускается в середине совпадения, мы всегда сопоставляем только внутренние, а не внешние кавычки. Но теперь мы не всегда хотим заменять пробелы - мы хотим делать это только в том случае, если был подстановочный знак. Это просто: проверьте, является ли группа 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"
.