【问题标题】:Running multiple regex patterns on String在字符串上运行多个正则表达式模式
【发布时间】:2014-12-27 11:18:34
【问题描述】:

假设我有一个List<String> 和一个空的List<Pattern>,这是将字符串中的单词转换为 Pattern 对象的最佳方法吗?

for(String word : stringList) {
    patterns.add(Pattern.compile("\\b(" + word + ")\\b);
}

然后稍后在字符串上运行它;

for(Pattern pattern : patterns) {
    Matcher matcher = pattern.matcher(myString);
    if(matcher.matches()) {
         myString = matcher.replaceAll("String[$1]");
    }
}

replaceAll 位只是一个示例,但在我使用它时,大部分时间都会使用 $1。

有没有更有效的方法?因为我觉得这有点笨拙。顺便说一下,我在列表中使用了 80 个字符串,虽然使用的字符串是可配置的,所以不会总是这么多。

这被设计成有点脏话过滤器,所以我会让你假设列表中的单词,

输入的一个例子是"You're a <curse>",这个词的输出是"You're a *****",虽然情况可能并不总是这样,在某些时候我可能会从HashMap<String, String>读取,其中关键是捕获组,值是替换。

例子:

if(hashMap.get(matcher.group(1)) == null) { 
    // Can't test if \ is required. Used it here for safe measure.
    matcher.replaceAll("\*\*\*\*");
 } else {
    matcher.replaceAll(hashMap.get(matcher.group(1));
 }

【问题讨论】:

  • @RealSkeptic,我在开头和结尾添加了 \b 来解决这个问题,这不会解决你提到的问题,因为 \b 是单词边界吗?
  • @AvinashRaj,更新了一个例子。
  • 这个词是否包含<> 符号?
  • 不应该这样做,但考虑到它的设计是可配置的,那么它可能在某个阶段。
  • 单词开头是否包含< 符号,那么\\b 将如何工作?

标签: java regex


【解决方案1】:

您可以将这些模式与| 交替使用:

Pattern pattern = Pattern.compile("\\b(" + String.join("|",stringList) + ")\\b");

如果您不能使用 Java 8,因此没有 String.join 方法,或者如果您需要 escape the words 以防止其中的字符被解释为正则表达式元字符,您将需要使用手动循环构建此正则表达式:

StringBuilder regex = new StringBuilder("\\b(");
for (String word : stringList) {
    regex.append(Pattern.quote(word));
    regex.append("|");
}
regex.setLength(regex.length() - 1); // delete last added "|"
regex.append(")\\b");
Pattern pattern = Pattern.compile(regex.toString());

要对不同的单词使用不同的替换,您可以使用以下循环应用模式:

Matcher m = pattern.matcher(myString);
StringBuilder out = new StringBuilder();
int pos = 0;
while (m.find()) {
    out.append(myString, pos, m.start());
    String matchedWord = m.group(1);
    String replacement = matchedWord.replaceAll(".", "*");
    out.append(replacement);
    pos = m.end();
}
out.append(myString, pos, myString.length());
myString = out.toString();

您可以以任何您喜欢的方式查找匹配词的替换。该示例生成与匹配单词长度相同的星号替换字符串。

【讨论】:

  • 请查看我为使自己更清楚而进行的编辑,但感谢 StringBuilder#setLength,不知道 StringBuilder 有这个!此外,对于不使用 Java 8 的人来说,还有 Google 的 Joiner,我知道这可能有点矫枉过正,但它可以帮助那些不了解它的人。
【解决方案2】:

Boann 的 Idee 已经不错了。但是例如对于日志过滤,我有一个很大的过滤器列表,文本与正则表达式匹配,我需要知道匹配的过滤器。因为我将其他过滤器(如模块、代码、级别等)也编码为正则表达式。如果有匹配,我会检查匹配的组。

1) 所以每一行只检查一次。

2) 由于所有正则表达式都构建在一个匹配器中,每个字符只检查一个。

这是从 N(条件数量)到接近 1(几乎任何数量的过滤器的常数)的极端改进。

public static void main(final String[] argc) throws Throwable {
    Config c;
    try(InputStream s = new FileInputStream("webapp/WEB-INF/logScanConfig.xml")) { c = (Config) JAXBContext.newInstance(Config.class).createUnmarshaller().unmarshal(s); }
    final LineContext[] a = c.rules.toArray(new LineContext[c.rules.size()]);
    final StringBuilder regex = new StringBuilder();
    for(int i=0;i<a.length;i++) {
        final LineContext e = a[i];
        final String p ="(^"+
                (e.modul == null?".*":e.modul)+" ; "+
                (e.code  == null?".*":e.code )+" ; "+
                (e.mesg  == null?".*":e.mesg )+" ; "+
                (e.level == null?".*":e.level)+" ; "+
                (e.regex == null?".*":e.regex)+"$)";
        if(regex.length()>0) regex.append("|");
        regex.append(p);
    }

    final Pattern pattern = Pattern.compile(regex.toString(), Pattern.DOTALL);
    final Matcher m = pattern.matcher("ISS ; 0025 ; 0008 ; I ; State Manager started");
    if(!m.matches()) {
        System.out.println("Not Found");
    } else {
        System.out.println("GroupCount: "+m.groupCount()+" A["+a.length+"]");
        for(int i=1;i<=m.groupCount();i++) {
            if(null != m.group(i)) {
                System.out.println("GROUP["+(i-1)+"]: "+m.group(i));
                System.out.println(a[i-1]);
            }
        }
    }
  }
}

这里是 logScanConfig.xml 的示例

<logScanConfig user="private.1" pass="private.2">
 <logUrls>
  <e>http://private.3:80/fetch/log</e>
  <e>http://private.4:80/fetch/log</e>
  <e>http://private.5:80/fetch/log</e>
 </logUrls>
 <rules>
  <e backlogTime='600' minCount='0' maxCount='0' modul='ART' code='0114' mesg='1007' level='E'><regex>.*ORA-27101: shared memory realm does not exist.*</regex></e>
  <e backlogTime='600' minCount='0' maxCount='0' modul='ISS' code='0098'             level='E'><regex>Insufficient memory .*</regex></e>
 </rules>
</logScanConfig>

【讨论】:

  • 能否请您添加一个logscanConfig,xml的示例
【解决方案3】:

如果不管word匹配什么,你都做同样的事情,你可以用你的词组成一个大的“OR”表达式,并使用一个单一的模式,像这样:

\\b(<word1>|<word2>|...|<wordN>)\\b

&lt;wordK&gt; 应循环替换为您的单词:

StringBuilder res = new StringBuilder("\\b(");
boolean first = true;
for(String word : stringList) {
    if (!first) {
        res.append("|");
    } else {
        first = false;
    }
    res.append(word);
}
res.append(")\\b");
Pattern p = Pattern.compile(res.toString());

注意:此解决方案假定单词没有正则表达式元字符。

【讨论】:

  • 感谢您的回复,请问您能看到我的编辑吗?
  • @charries96 编辑呢?这与您的代码的功能兼容,但只有一个表达式。
  • 没关系,以为它改变了事情,但猜它没有。早上好困。
猜你喜欢
  • 2011-02-17
  • 1970-01-01
  • 2015-11-28
  • 2020-05-05
  • 1970-01-01
  • 1970-01-01
  • 2015-01-11
  • 2015-07-08
  • 2021-12-08
相关资源
最近更新 更多