【问题标题】:Replacing spaces within quotes替换引号内的空格
【发布时间】:2010-12-19 06:29:27
【问题描述】:

我真的在这里与正则表达式作斗争。使用 Java,我将如何用另一个字符(或转义空格 "\ ")替换引号内的所有空格(实际上是双引号),但前提是短语以通配符结尾。

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

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

【问题讨论】:

    标签: java regex


    【解决方案1】:

    我认为最好的解决方案是使用正则表达式查找所需的引用字符串,然后替换正则表达式匹配项中的空格。像这样的:

    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(" ", "\\\\ "),用反斜杠和空格替换匹配项(带引号的字符串)中的每个空格。 (这个字符串是\\——为什么需要两个真正的反斜杠,我不确定。)如果你以前没有见过appendReplacementappendTail(我没有),这就是它们的作用:串联,它们遍历整个字符串,替换与appendReplacement 的第二个参数匹配的任何内容,并将其全部附加到给定的StringBufferappendTail 调用对于捕获最后不匹配的内容是必要的。 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"

    【讨论】:

    • 是的,太棒了!我不知道 appendReplacement/appendTail。我无法告诉你们我对你们所有人帮助我的感激之情。这让我发疯了!谢谢大家!!!
    • 对于反例a AND "b" AND *"c",上面的代码不起作用,因为正则表达式无法区分引号的insideoutside。正则表达式不能做所有事情,否则它们不会被称为 regular.
    • @Roland:确实他们不能。幸运的是,从问题的描述来看,这种形式的输入听起来不太可能发生。可以通过简单地始终匹配引号来避免这种情况,然后仅在它们以通配符结尾时才进行替换。在这种情况下,假设输入有效,这将起作用。
    • @Roland:我已经通过 always 匹配引号修复了代码以使其能够有效地解决该问题(因此正则表达式永远没有机会扫描匹配的外部谢谢其“匹配所有”程序总是向前推进)。我相信只要引号始终匹配,这就是可靠的(我认为正则表达式无法检查,但听起来像是假设的),但如果您认为我遗漏了什么,请告诉我。
    【解决方案2】:

    你真的需要正则表达式吗?该任务似乎描述得很好,但对于正则表达式来说有点太复杂了。所以我宁愿明确地编程出来。

    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"));
      }
    }
    

    【讨论】:

    • 非常感谢您的帮助。该解决方案很有魅力,但我也喜欢看到 appendTail/appendReplacement 组合!非常感谢!!!
    【解决方案3】:

    有效吗?

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

    我现在没有 IDE,也没有测试它

    【讨论】:

    • 这不符合要求,因为发帖人需要选择性地将空格改为斜线+空格。
    猜你喜欢
    • 1970-01-01
    • 2019-01-06
    • 2018-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-31
    • 1970-01-01
    相关资源
    最近更新 更多