【问题标题】:Pattern Matcher Vs String Split, which should I use?模式匹配器与字符串拆分,我应该使用哪个?
【发布时间】:2013-10-16 17:19:29
【问题描述】:

第一次发帖。

首先我知道如何使用模式匹配器和字符串拆分。 我的问题是哪个最适合我在我的示例中使用,为什么? 或提供更好的替代方案的建议。

任务: 我需要在未知字符串中的两个已知正则表达式之间提取未知名词。

我的解决方案: 获取名词的开头和结尾(来自 Regexp 1&2)和子字符串以提取名词。

String line = "unknownXoooXNOUNXccccccXunknown";
int goal = 12 ;
String regexp1 = "Xo+X";
String regexp2 = "Xc+X";
  1. 我需要在第一个正则表达式之后定位索引位置。
  2. 我需要在第二个正则表达式之前找到索引位置。

A) 我可以使用模式匹配器

    Pattern p = Pattern.compile(regexp1);
    Matcher m = p.matcher(line);
    if (m.find()) {
        int afterRegex1 = m.end();
    } else {
        throw new IllegalArgumentException();
        //TODO Exception Management;
    }

B) 我可以使用字符串拆分

    String[] split = line.split(regex1,2);
    if (split.length != 2) {
        throw new UnsupportedOperationException();
        //TODO Exception Management;
    }
    int afterRegex1 = line.indexOf(split[1]);

我应该使用哪种方法,为什么? 我不知道哪个在时间和内存上更有效。 两者都足够接近我自己的可读性。

【问题讨论】:

  • 为什么不将所有内容组合成一个正则表达式,然后使用匹配组提取名词?
  • Matcher.groupCount(); 不计算出现次数!所以这不是String.split()的替代品。
  • @Holger 是正确的。您应该使用Pattern.split() 或循环计算m.find() 返回true 的次数。
  • 请注意,UnsupportedOperationException 在这里没有意义;这是典型的IllegalArgumentException
  • @maaartinus 谢谢我做了你的改变,这就是我有//TODO Exception Management;的原因我需要了解更多关于正确用法的信息。

标签: java regex string performance split


【解决方案1】:

我会这样做:

String line = "unknownXoooXNOUNXccccccXunknown";
String regex = "Xo+X(.*?)Xc+X";

Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(line);
if (m.find()) {
   String noun = m.group(1);
}

(.*?) 用于在名词 reluctant 上进行内部匹配。这可以防止我们的结尾模式再次出现在字符串的未知部分中。

编辑

这是因为(.*?) 定义了一个捕获组。模式中只定义了一个这样的组,因此它的索引为 1(m.group(1) 的参数)。这些组从 1 开始从左到右进行索引。如果模式是这样定义的

String regex = "(Xo+X)(.*?)(Xc+X)";

那么会有三个捕获组,这样

m.group(1); // yields "XoooX"
m.group(2); // yields "NOUN"
m.group(3); // yields "XccccccX"

组 0,但它匹配整个模式,它相当于这个

m.group(); // yields "XoooXNOUNXccccccX"

有关您可以使用 Matcher 做什么的更多信息,包括在源字符串中获取模式的开始和结束位置的方法,请参阅Matcher JavaDocs

【讨论】:

  • 在这个例子中,名词是否也包含不需要的登机正则表达式?
  • 没有。它不会。该组只是括号中的部分。
  • 巫术!它有效,但我不知道为什么。 regexp = "Xo+X(.*)Xc+X" 将匹配 XoooXNOUNXccccccXin 行。它如何(或链接我)提供名词?正则表达式中 (0)(1)(2) 的分组,这就是你的魔法的答案。我可以使用m.group(1) 选择要返回正则表达式的哪个部分吗?
  • 值得注意的是,这仅在字符串后面没有其他 Xc+X 实例的情况下才能正确捕获 NOUN。如果字符串恰好是“unknownXoooXNOUNXccccccXunknownXcX”,您将捕获“NOUNXccccccXunknown”。我只提到这一点是因为字符串是“未知的”——所以它可能会发生。将您的正则表达式更新为 "Xo+X(.*?)Xc+X" 应该可以解决此问题。
  • 很好,@BunjiquoBianco。我将编辑答案以反映这一点。
【解决方案2】:

您应该使用String.split() 来提高可读性,除非您处于一个紧凑的循环中。

根据split()'s javadocsplit() 相当于Pattern.compile(),如果您处于紧密循环中,您可以优化掉它。

【讨论】:

  • 我很好奇哪个表现更好:Matcher 或 split,这就解释了为什么 Matcher(带有预编译模式)优于 split。
【解决方案3】:

您似乎想要获得一个独特的事件。为此,只需

input.replaceAll(".*Xo+X(.*)Xc+X.*", "$1")

为了效率,请改用Pattern.matcher(input).replaceAll

如果您输入的内容包含换行符,请使用Pattern.DOTALLs 修饰符。


如果您想使用拆分,请考虑使用 Guava 的Splitter。它的行为更加理智,还接受Pattern,这对速度有好处。

【讨论】:

    【解决方案4】:

    如果你真的需要这些位置,你可以这样做:

    String line = "unknownXoooXNOUNXccccccXunknown";
    String regexp1 = "Xo+X";
    String regexp2 = "Xc+X";
    
    Matcher m=Pattern.compile(regexp1).matcher(line);
    if(m.find())
    {
      int start=m.end();
      if(m.usePattern(Pattern.compile(regexp2)).find())
      {
        final int end = m.start();
        System.out.println("from "+start+" to "+end+" is "+line.substring(start, end));
      }
    }
    

    但如果您只需要介于两者之间的词,我推荐 Ian McLaird 所展示的方式。

    【讨论】:

    • 这会起作用,但一般来说,OP 的方法存在一个(潜在的)问题。如果regexp2 出现在源字符串中之前 regexp1(可能在开头的未知文本中),会发生什么?在实践中可能不会出现,但这个想法让我有点痒。
    • @Ian McLaird:因为我重新使用了Matcher,它会在上一场比赛结束时开始搜索。它按预期工作。
    • @Holger 基本上是我实现的版本,但触摸更优雅。 m.usePattern 这比我做的要整洁得多。我将保留它以备将来使用。但是你的正确我只需要这个词,而不是位置。我只是用它来实现我的目标。
    • @IanMcLaird 关于正则表达式 1&2 不匹配,由于我提取字符串的方式,这不应该发生在我的实现中,这已经被考虑过了。谢谢
    猜你喜欢
    • 1970-01-01
    • 2021-11-22
    • 2014-02-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-01
    相关资源
    最近更新 更多