【问题标题】:Negative lookahead in regex which matches everything accept a specific character正则表达式中匹配所有内容的负前瞻接受特定字符
【发布时间】:2017-12-20 16:11:06
【问题描述】:

我在使用 Java 中的负前瞻定义正则表达式时遇到问题。

给定以下字符串:

Today [#[#item#] was|the items were#] shipped so [#it is|they are#] gone.

我正在尝试根据某个值将此字符串转换为以下形式之一(是的,这是一种区分单数和复数形式的方法):

Today [#item#] was shipped so it is gone.Today the items were shipped so they are gone

我正在尝试在 Java 中使用正则表达式来匹配此模式并实现此转换:

public String convert(String text, boolean isSingular) {
    Pattern spPattern = Pattern.compile("\\[#.*?\\|.*?#\\]");
    Matcher matcher = spPattern.matcher(text);
    while (matcher.find()) {
        int start = matcher.start()+2;
        int end = matcher.end()-2;
        int indexOfPipe = text.indexOf("|", start);
        String replacement = (isSingular) ? text.substring(start, indexOfPipe) : text.substring(indexOfPipe+1, end);
        text = matcher.replaceFirst(replacement);
        matcher = spPattern.matcher(text);
     }
}

对于单数形式:while-loop 第一次迭代后textToday [#item#] was shipped so [#it is|they are#] gone.,这样就可以了。然而,在第二次迭代中,Matcher 匹配组[#item#] was shipped so [#it is|they are#],而它应该是[#it is|they are#]。我很确定我需要某种负面的前瞻性。

我已经尝试了以下模式,但它似乎没有做任何事情:

(\\[#.*?\\|.*?#\\])(?!\\[#[^\\|]*?#\\])(“尝试匹配 [# 和 #] 之间的所有内容,接受那些标签之间不包含 | 的情况”)

我错过了什么?

【问题讨论】:

  • 你知道java.text.MessageFormat已经提供了这个功能吗?
  • @VGR :对于这个特定的示例,它应该可以工作,是的。但是,我正在实施的系统还有一些其他要求,这使得使用 java.text.MessageFormat 有点痛苦。

标签: java regex negative-lookahead


【解决方案1】:

简介

您遇到的问题是因为您的第一个替换是生成Today [#item#] was shipped so [#it is|they are#] gone.,而您的正则表达式匹配[#item#] was shipped so [#it is|they are#]。然后,您的正则表达式会错误地替换此字符串。

true 解决这个问题的方法是创建一个解析器,但是如果函数以 递归方式运行正则表达式(它 有点感谢while循环);所以这个答案也适用于[#[#a|b#]|b#],但请注意,任何进一步的嵌套都会失败(如果它嵌套在单数一侧)。


代码

See regex in use here

\[#((?:\[#.*?#]|(?!#])[^|])*?)\|((?:\[#.*?#]|(?!#])[^|])*?)#]

用法

See code in use here

import java.util.*;
import java.lang.*;
import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Ideone
{
    public static void main (String[] args) throws java.lang.Exception
    {
        String s = "Today [#[#item#] was|the items were#] shipped so [#it is|they are#] gone.";
        System.out.println(convert(s, true));
        System.out.println(convert(s, false));
    }

    public static String convert(String text, boolean isSingular) {
        Pattern spPattern = Pattern.compile("\\[#((?:\\[#.*?#]|(?!#])[^|])*?)\\|((?:\\[#.*?#]|(?!#])[^|])*?)#]");
        Matcher matcher = spPattern.matcher(text);
        while (matcher.find()) {
            String replacement = isSingular ? matcher.group(1) : matcher.group(2);
            text = matcher.replaceFirst(replacement);
            matcher = spPattern.matcher(text);
        }
        return text;
    }
}

说明

  • \[# 匹配 [# 字面意思
  • ((?:\[#.*?#]|(?!#])[^|])*?) 将以下内容捕获到捕获组 1
    • (?:\[#.*?#]|(?!#])[^|])*? 匹配任意一个跟随任意次数,但尽可能少
    • \[#.*?#] 匹配以下
      • \[# 匹配 [# 字面意思
      • .*? 匹配任意字符任意次数,但越少越好
      • #] 逐字匹配
    • (?!#])[^|] 匹配以下
      • (?!#]) 负前瞻确保后面的内容与 #] 字面上的不匹配
      • [^|] 匹配除| 之外的任何内容
  • \| 匹配 | 字面意思
  • ((?:\[#.*?#]|(?!#])[^|])*?) 将以下内容捕获到捕获组 2
    • 参见捕获组 1 下的说明(这与捕获组 1 相同)
  • #] 字面上匹配

【讨论】:

  • 解析器注释+1。我没有想到这一点(我最后一次修改解析器是大约 15 年前在大学里),但如果我有时间的话可能会尝试一下。同时,您的正则表达式非常适合我的用例。
【解决方案2】:

这是一个伪代码,展示了一种可以做到这一点的方法。
显然我不懂Java。

regex_main = "(?s)(.*?)((?=\\[\\#)(?:(?=.*?\\[\\#(?!.*?\\3)(.*\\#\\](?!.*\\4).*))(?=.*?\\#\\](?!.*?\\4)(.*)).)+?.*?(?=\\3)(?:(?!\\[\\#).)*)(?=\\4)|(.+)"

regex_brack_contents = "(?s)^\\[\\#(.*)\\#\\]$"

sTemplate = "Today [#[#item#] was|the items were#] shipped so [#it is|they are#] gone."
sOut[5] = ""
nPermutations = 0
Matcher _M = regex_main.matcher( sTemplate );

while ( _M.find() ) {
    if ( _M.group(1) ) {
        for (i = 0; i < 5; i++ ) 
            sOut[i] += _M.group(1)
        Matcher _m = regex_brack_contents.matcher( _M.group(2) )
        if ( _m.find() ) {
            aray = _m.group(1).split("|");
            for ( i = 0; i < sizeof(aray), i < 5; i++ )
                sOut[i] += aray[i]
            if ( i > nPermutations )
                nPermutations = i
        }
    }
    else { 
        for (i = 0; i < 5; i++ ) 
           sOut[i] += _M.group(5)
    }
    _M = regex_main.matcher( sTemplate );
}
for (i = 0; i < nPermutations; i++ ) 
    print( sOut[i] + "\r\n" )

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-05
    • 1970-01-01
    • 1970-01-01
    • 2015-08-22
    相关资源
    最近更新 更多