Java Regex:替换字符,除非前面有其他字符

时间:2012-09-07 11:39:17

标签: java regex

我正在使用Java和正则表达式,需要将一些数据拆分为多个实体。在我的输入中,单引号字符(')指定实体的结尾,除非其前面是转义字符,它是一个问号(?)。

我的RegEx是(?<!\\?)\\',我正在使用扫描仪将输入拆分为单独的实体。因此以下案例正常运作:

Hello'There  becomes 2 entities: Hello and There
Hello?'There remains 1 entity:   Hello?'There

然而,当我遇到我想逃避问号的情况时,它不起作用。所以:

Hello??'There     should become 2 entities:   Hello?? and There
Hello???'There    should become 1 entity:     Hello???'There
Hello????'There   should become 2 entities:   Hello???? and There
Hello?????'There  should become 1 entity:     Hello????'There
Hello?????There   should become 1 entity:     Hello????There
Hello??????There  should become 1 entity:     Hello?????There

因此,规则是如果存在偶数个问号,后跟引号,则应该拆分。如果存在奇数个问号,那么它不应该分开。

有人可以帮我修复我的正则表达式(希望有解释!)来处理多个案例吗?

谢谢,

菲尔

3 个答案:

答案 0 :(得分:3)

尝试使用此表达式来匹配偶数情况:(?<=[^\?](?>\?\?){0,1000})'

  • (?<=...)'是一个积极的外观,即每个'前面有(?<=)之间的表达式匹配
  • (?>\?\?)是一个包含2个连续问号的原子组
  • (?>\?\?){0,1000}表示可以有0到1000个这样的组。请注意,您不能编写(?>\?\?)*,因为表达式需要具有最大长度(最大组数)。但是,您应该能够大量增加上限,具体取决于表达式的其余部分
  • [^\?](?>\?\?)...表示2个问号的组必须以某个字符开头,但不能带问号(否则您将匹配奇数情况)

答案 1 :(得分:2)

请勿使用split()。这似乎是一个明显的解决方案,但匹配实体本身要比匹配分隔符更容易。大多数启用正则表达式的语言都有内置的方法,比如Python的findall()或Ruby的scan(),但在Java中我们仍然坚持编写样板文件。这是一个例子:

Pattern p = Pattern.compile("([^?']|\\?.)+");
String[] inputs = {
    "Hello??'There",
    "Hello???'There",
    "Hello????'There",
    "Hello?????'There",
    "Hello?????There",
    "Hello??????There"
};
for (String s : inputs)
{
  System.out.printf("%n%s :%n", s);
  Matcher m = p.matcher(s);
  while (m.find())
  {
    System.out.printf("  %s%n", m.group());
  }
}

输出:

Hello??'There :
  Hello??
  There

Hello???'There :
  Hello???'There

Hello????'There :
  Hello????
  There

Hello?????'There :
  Hello?????'There

Hello?????There :
  Hello?????There

Hello??????There :
  Hello??????There

任意最大长度的噱头托马斯使用,除了是一个恶心的黑客(没有进攻意图,托马斯!),是不可靠的,因为他们不断引入处理这些东西的Pattern.java代码中的错误。但是,不要将此解决方案视为另一种解决方法; lookbehinds永远不应该是你的第一个手段,即使是像.NET这样可靠且无限制地工作的风格。

答案 2 :(得分:0)

您确定要使用正则表达式吗?如果您的字符串相对较小和/或执行时间不是一个大问题,您可以使用字符串生成器和循环来计算&#34;?&#34; e.g。

    //Your String
    String x = "Hello??'World'Hello?'World";
    StringBuilder sb = new StringBuilder();
    //Holds your splits
    ArrayList<String> parts = new ArrayList<String>();

    int questionmarkcount = 0;
    int _isEven;

    for (char c : x.toCharArray()) {
        if (c == '?') {
            questionmarkcount++;
            sb.append(c);
        } else if (c == '\'') {
            _isEven = questionmarkcount % 2;
            //if there are an even number of '? or none
            if (_isEven == 0 || questionmarkcount == 0) {
                //add the current split, reset the ? count and clear the String builder
                parts.add(sb.toString());
                sb.delete(0, sb.length());
                questionmarkcount = 0;
            } else {
                //append the question mark, no split is needed
                sb.append(c);
                //start counting from the beginning
                questionmarkcount = 0;
            }
        } else {
            sb.append(c);
        }
    }
    parts.add(sb.toString());

在循环结束时,部件ArrayList将保存所有拆分。如果在&#39;之前有偶数个问号,则当前代码将被拆分。