正则表达式删除除了某个前缀后面的所有方括号

时间:2011-10-06 22:48:05

标签: java regex regex-negation

所以,我有一个字符串。大多数情况下,如果字符串中包含方括号,则会发生不好的事情。但是,在少数情况下,必须保留括号。需要保留的这些括号由特定前缀标识。例如,如果字符串是:

  

apple] [s [pears]前缀:[oranges] lemons] persimmons [pea [ches ap] ricots [] [[]]] []

我想把它变成:

  苹果梨前缀:[橙子]柠檬柿子桃子杏子

我想出了一个Rube Goldberg混乱的解决方案,看起来像这样:

public class Debracketizer
{
    public static void main( String[] args )
    {
        String orig = "apples [pears] prefix:[oranges] lemons ]persimmons[ pea[ches ap]ricots";
        String result = debracketize(orig);
        System.out.println(result);
    }

    private static void debracketize( String orig )
    {
        String result1 = replaceAll(orig,
                                    Pattern.compile("\\["), 
                                    "",
                                    ".*prefix:$");

        String result2 = replaceAll(result1,
                                    Pattern.compile("\\]"),
                                    "",
                                    ".*prefix:\\[[^\\]]+$");

        System.out.println(result2);
    }

    private static String replaceAll( String orig, Pattern pattern, 
                                      String replacement, String skipPattern )
    {
        String quotedReplacement = Matcher.quoteReplacement(replacement);
        Matcher matcher = pattern.matcher(orig);
        StringBuffer sb = new StringBuffer();
        while( matcher.find() )
        {
            String resultSoFar = orig.substring(0, matcher.start());
            if (resultSoFar.matches(skipPattern)) {
                matcher.appendReplacement(sb, matcher.group());
            } else {
                matcher.appendReplacement(sb, quotedReplacement);
            }
        }
        matcher.appendTail(sb);
        return sb.toString();
    }
}

我确信必须有更好的方法来做到这一点 - 最好是使用一个简单的正则表达式和一个简单的String.replaceAll()。但是我无法想出来。

(之前我问partial version of this question,但我看不出如何调整完整案例的答案。这将教会我提出部分问题。)

6 个答案:

答案 0 :(得分:1)

不要采用正则表达式的方式,因为那条路将永远变暗。考虑以下或其变型。根据合理的分隔符拆分字符串(可能是“前缀[”)并且要聪明地删除其余的大括号。

这是一个关闭袖带算法(StringUtils是org.apache.commons.lang.StringUtils):

  1. 按“prefix [”拆分字符串。 StringUtils.splitByWholeSeparator()似乎是一个很好的候选者(在此,返回值存储在blam中)。
  2. 从结果字符串中删除所有“[”字符。也许做StringUtils.stripAll(blam)
  3. 对于blam中的每个字符串,请执行以下操作:
    1. 如果是第一个字符串,则删除所有“]”字符。 StringUtils.strip(blam[0], ']');。用这个字符串替换blam [0]。
    2. 如果不是第一个字符串,
    3. 使用分隔符']'拆分字符串(在此处,返回值存储在kapow中)。
    4. 根据kapow的每个元素构造一个字符串(名为smacky)。添加第0个元素后,将附加']'添加到smacky。
    5. 用smacky替换blam [index]的字符串。
  4. 通过在blam数组中附加所有字符串来构造最终结果。
  5. 跳出幸福的跳汰机。

答案 1 :(得分:1)

这一个班轮:

String resultString = subjectString.replaceAll("(?<!prefix:(?:\\[\\w{0,2000000})?)[\\[\\]]", "");

当适用于:apple] [s [pears]前缀:[oranges] lemons] persimmons [pea [ches ap] ricots [] [[]]] []

会给你你想要的结果:

apples pears prefix:[oranges] lemons persimmons peaches apricots 

您唯一的限制是前缀:[]之间的单词可以拥有的最大字符数。在这种情况下,限制是2000000.限制来自java,因为它不支持负面看后方的无限重复。

答案 2 :(得分:1)

有趣的问题。这是一个替代测试解决方案,不使用lookbehind。

public class TEST
{
    public static void main( String[] args )
    {
        String orig = "apples [pears] prefix:[oranges] lemons ]persimmons[ pea[ches ap]ricots";
        String result = debracketize(orig);
        System.out.println(result);
    }

    private static String debracketize( String orig )
    {
        String re = // Don't indent to allow wide regex comments.
"(?x)                         # Set free-spacing mode.            \n" +
"# Either capture (and put back via replace) stuff to be kept...  \n" +
"  (                          # $1: Stuff to be kept.             \n" +
"    prefix:\\[[^\\[\\]]+\\]  # Either the special sequence,      \n" +
"  | (?:                      # or...                             \n" +
"      (?!                    # (Begin negative lookahead.)       \n" +
"        prefix:              # If this is NOT the start          \n" +
"        \\[[^\\[\\]]+\\]     # of the special sequence,          \n" +
"      )                      # (End negative lookahead.)         \n" +
"      [^\\[\\]]              # then match one non-bracket char.  \n" +
"    )+                       # Do this one char at a time.       \n" +
"  )                          # End $1: Stuff to be kept.         \n" +
"| # Or... Don't capture stuff to be removed (un-special brackets)\n" +
"  [\\[\\]]+                  # One or more non-special brackets.";
        return orig.replaceAll(re, "$1");
    }
}

此方法使用两个全局替代方法。第一种方法是捕获(然后替换)特殊序列和非括号字符,第二种方法匹配(并删除)非特殊括号。

答案 3 :(得分:0)

如果你有一对你并不担心出现在原始字符中的字符(例如<>),那么你可以先将你希望保留的方括号翻译成这些,去掉余数,然后改回翻译的括号。

这是ruby(移植到java希望不是太难,你只需要用捕获组进行全局搜索替换):

>> s = 'apple][s [pears] prefix:[oranges] lemons ]persimmons[ pea[ches ap]ricots [][[]]][]'
=> "apple][s [pears] prefix:[oranges] lemons ]persimmons[ pea[ches ap]ricots [][[]]][]"
>> s.gsub(/([^\[\]]+):\[([^\[\]]+)\]/, '\1:<\2>').gsub(/[\[\]]/,'').gsub(/</,'[').gsub(/>/,']')
=> "apples pears prefix:[oranges] lemons persimmons peaches apricots "

答案 4 :(得分:0)

1找出与prefix:\[[^\]]+\]

匹配的匹配项

2使用相同的正则表达式来分割字符串

每个数组元素

3,删除]或[(你的例子有两个元素)

4将元素与步骤1中的结果连接起来。

答案 5 :(得分:0)

这是你的正则表达式解决方案:

input.replaceAll("((?<!prefix:)\\[(?!oranges)|(?<!prefix:\\[oranges)\\])", "");

它使用两个负面外观来防止删除受保护前缀周围的方括号。如果您想保护多个字词,可以通过在正则表达式中将oranges更改为(oranges|apples|pears)来实现此目的。

以下是使用您的数据的测试:

public static void main(String... args) throws InterruptedException {
     String input = "apple][s [pears] prefix:[oranges] lemons ]persimmons[ pea[ches ap]ricots [][[]]][]";
     String result = input.replaceAll("((?<!prefix:)\\[(?!oranges)|(?<!prefix:\\[oranges)\\])", "");
     System.out.println(result);
}

输出:

apples pears prefix:[oranges] lemons persimmons peaches apricots
相关问题