【问题标题】:How to match against multiple regular expressions and determine which expression matched in Java如何匹配多个正则表达式并确定Java中匹配的表达式
【发布时间】:2012-04-12 14:38:29
【问题描述】:

我有一个 Java 程序,它逐行遍历文件,并尝试将每一行与四个正则表达式之一进行匹配。根据匹配的表达式,程序执行特定操作。这是我所拥有的:

private void processFile(ArrayList<String> lines) {
    ArrayList<Component> Components = new ArrayList<>();
    Pattern pattern = Pattern.compile(
            "Object name\\.{7}: (.++)|"
            + "\\{CAT=([^\\}]++)\\}|"
            + "\\{CODE=([^\\}]++)\\}|"
            + "\\{DESC=([^\\}]++)\\}");

    Matcher matcher;
    // Go through each line and see if the line matches the any of the regexes
    // defined
    Component currentComponent = null;

    for (String line : lines) {
        matcher = pattern.matcher(line);

        if (matcher.find()) {
            // We found a tag. Find out which one
            String match = matcher.group();

            if (match.startsWith("Obj")) {
                // We've got the object name
                if (currentComponent != null) {
                    Components.add(currentComponent);
                }
                currentComponent = new Component();
                currentComponent.setName(matcher.group(1));
            } else if (currentComponent != null) {
                if (match.startsWith("{CAT")) {
                    currentComponent.setCategory(matcher.group(2));
                } else if (match.startsWith("{CODE")) {
                    currentComponent.setOrderCode(matcher.group(3));
                } else if (match.startsWith("{DESC")) {
                    currentComponent.setDescription(matcher.group(4));
                }
            }
        }
    }

    if (currentComponent != null) {
        Components.add(currentComponent);
    }
}

如您所见,我已将四个正则表达式合二为一,并将整个正则表达式应用于该行。如果找到匹配项,我会检查字符串的开头以确定匹配的表达式,然后从组中提取数据。如果有人有兴趣运行代码,下面提供了一些示例数据:

Object name.......: PMF3800SN
Last modified.....: Wednesday 9 November 2011 11:55:04 AM
File offset (hex).: 00140598 (Hex).
Checksum (hex)....: C1C0 (Hex).
Size (bytes)......: 1,736
Properties........: {*DEVICE}
                    {PREFIX=Q}
                    {*PROPDEFS}
                    {PACKAGE="PCB Package",PACKAGE,1,SOT-323 MOSFET}
                    {*INDEX}
                    {CAT=Transistors}
                    {SUBCAT=MOSFET}
                    {MFR=NXP}
                    {DESC=N-channel TrenchMOS standard level FET with ESD protection}
                    {CODE=1894711}
                    {*COMPONENT}

                    {PACKAGE=SOT-323 MOSFET}
                    *PINOUT SOT-323 MOSFET
                    {ELEMENTS=1}
                    {PIN "D" = D}
                    {PIN "G" = G}
                    {PIN "S" = S}

虽然我的代码有效,但我不喜欢稍后在调用 startsWith 例程时重复部分字符串的事实。

我很想知道其他人会如何写这篇文章。

Amr

【问题讨论】:

  • 您是否有理由不创建四个不同的正则表达式实例并在每一行上一个接一个地运行它们直到一个匹配?
  • @DanielHilgarth 我认为这不是一个可行的解决方案。
  • 这可能会解决您的问题。 stackoverflow.com/questions/895279/…
  • 不可行?你有这样的想法吗? (您是否有理由认为,您尝试以不同方式做的任何事情都会以任何其他方式在幕后发挥作用?)
  • 您还可以在不更改当前匹配位置的情况下将新模式分配给现有匹配器 (ref)。在某些情况下,这是一个非常有用的功能,但我认为这不是其中之一。如果没有增加足够的价值来证明它的合理性,它会使代码变得相当复杂。

标签: java regex


【解决方案1】:

group() 为匹配失败的组返回null。因此,您可以将子表达式分组并在匹配后检查它们是否有nulls:

Pattern pattern = Pattern.compile(
         "(Object name\\.{7}: (.++))|"
         + "(\\{CAT=([^\\}]++)\\})|"
         + "(\\{CODE=([^\\}]++)\\})|"
         + "(\\{DESC=([^\\}]++)\\})"); 
...
if (match.group(1) != null) { // Object ...
    ...
} ...

实际上,如果您的子表达式中没有 |s,您甚至可以对现有组执行此操作。

【讨论】:

    【解决方案2】:

    正如@axtavt 所指出的,您可以直接发现一个小组是否参加了比赛。您甚至不必更改正则表达式;对于每个备选方案,您已经有一个捕获组。我喜欢在测试中使用start(n) 方法,因为它看起来更简洁,但是检查group(n) 的空值(就像@axtavt 所做的那样)会产生相同的结果。这是一个例子:

    private static void processFile(ArrayList<String> lines) {
    
        Pattern p = Pattern.compile(
                "Object name\\.{7}: (.++)|"
                + "\\{CAT=([^\\}]++)\\}|"
                + "\\{CODE=([^\\}]++)\\}|"
                + "\\{DESC=([^\\}]++)\\}");
    
        // Create the Matcher now and reassign it to each line as we go.
        Matcher m = p.matcher("");
    
        for (String line : lines) {
            if (m.reset(line).find()) {
                // If group #n participated in the match, start(n) will be non-negative.
                if (m.start(1) != -1) {
                    System.out.printf("%ncreating new component...%n");
                    System.out.printf("  name: %s%n", m.group(1));
                } else if (m.start(2) != -1) {
                    System.out.printf("  category: %s%n", m.group(2));
                } else if (m.start(3) != -1) {
                    System.out.printf("  order code: %s%n", m.group(3));
                } else if (m.start(4) != -1) {
                    System.out.printf("  description: %s%n", m.group(4));
                }
            }
        }
    }
    

    但是,我不确定我是否同意您关于在代码中重复部分字符串的推理。如果数据格式发生变化,或者您更改了提取的字段,那么更新代码时似乎更容易不同步。换句话说,您当前的代码不是多余的,它是自记录的。 :D

    编辑:您在评论中提到了一次处理整个文件而不是逐行处理的可能性。这实际上是更简单的方法:

    private static void processFile(String contents) {
    
        Pattern p = Pattern.compile(
                "Object name\\.{7}: (.++)|"
                + "\\{CAT=([^\\}]++)\\}|"
                + "\\{CODE=([^\\}]++)\\}|"
                + "\\{DESC=([^\\}]++)\\}");
    
        Matcher m = p.matcher(contents);
    
        while (m.find()) {
            if (m.start(1) != -1) {
                System.out.printf("%ncreating new component...%n");
                System.out.printf("  name: %s%n", m.group(1));
            } else if (m.start(2) != -1) {
                System.out.printf("  category: %s%n", m.group(2));
            } else if (m.start(3) != -1) {
                System.out.printf("  order code: %s%n", m.group(3));
            } else if (m.start(4) != -1) {
                System.out.printf("  description: %s%n", m.group(4));
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      我会定义一个模式 + 可运行的元对象。遍历行,然后遍历元对象。如果匹配,则执行可运行文件。比如,

      class Meta {
        Pattern pattern;
        Runnable runnable;
        Matcher matcher;
      
        Meta(Pattern p, Runnable r) {
          pattern = p;
          runnable = r;
        }
      }
      
      Meta[] metas = new Meta[] { new Meta(Pattern.compile(...), new Runnable() { ... }), new Meta(...), ... };
      
      
      for (String line : lines) {
        for (Meta meta : metas) {
          final Matcher matcher = meta.pattern.matcher(line);
          if (matcher.matches()) {
            meta.matcher = matcher;
            meta.runnable.run();
          }
        }
      }
      

      这是“对象”行的元对象的样子,

      Meta m = new Meta(Pattern.compile("Object name\\.{7}: (.++)", new Runnable() {
        // We've got the object name
        if (currentComponent != null) {
          Components.add(currentComponent);
        }
        currentComponent = new Component();
        currentComponent.setName(matcher.group(1));
      });
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-03-30
        • 2011-05-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多