Замена пробелов в кавычках - PullRequest
1 голос
/ 18 декабря 2010

Я действительно борюсь с регулярным выражением здесь. Используя Java, как бы я заменил все пробелы в кавычках (на самом деле двойные кавычки) другим символом (или экранированным пробелом "\ "), но ТОЛЬКО если фраза заканчивается символом подстановки.

word1 AND "word2 word3 word4*" OR "word5 word6" OR word7

до

word1 AND "word2\ word3\ word4*" OR "word5 word6" OR word7

Ответы [ 3 ]

2 голосов
/ 18 декабря 2010

Вам действительно нужны регулярные выражения здесь? Задача кажется хорошо описанной, но слишком сложной для регулярных выражений. Поэтому я бы предпочел запрограммировать это явно.

package so4478038;

import static org.junit.Assert.*;

import org.junit.Test;

public class QuoteSpaces {

  public static String escapeSpacesInQuotes(String input) {
    StringBuilder sb = new StringBuilder();
    StringBuilder quotedWord = new StringBuilder();
    boolean inQuotes = false;
    for (int i = 0, imax = input.length(); i < imax; i++) {
      char c = input.charAt(i);
      if (c == '"') {
        if (!inQuotes) {
          quotedWord.setLength(0);
        } else {
          String qw = quotedWord.toString();
          if (qw.endsWith("*")) {
            sb.append(qw.replace(" ", "\\ "));
          } else {
            sb.append(qw);
          }
        }
        inQuotes = !inQuotes;
      }
      if (inQuotes) {
        quotedWord.append(c);
      } else {
        sb.append(c);
      }
    }
    return sb.toString();
  }

  @Test
  public void test() {
    assertEquals("word1 AND \"word2\\ word3\\ word4*\" OR \"word5 word6\" OR word7", escapeSpacesInQuotes("word1 AND \"word2 word3 word4*\" OR \"word5 word6\" OR word7"));
  }
}
2 голосов
/ 18 декабря 2010

Я думаю, что лучшее решение - использовать регулярное выражение для поиска нужных строк в кавычках, а затем заменить пробелы внутри совпадения регулярного выражения. Примерно так:

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".

0 голосов
/ 18 декабря 2010

это работает?

str.replaceAll("\"", "\\");

У меня сейчас нет IDE, и я не тестирую его

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...