如何从列表中检测到“Apple 1& 2”等单词并将其分为“Apple 1”和“Apple 2”?

时间:2010-05-25 15:58:40

标签: regex normalization

输入

Papaya 2
Apple 1 & 2
Orange 1, 2 & 3
Kiwi 1 - 4
Banana1-4
Breadfruit

期望的输出

Papaya 2
Apple 1
Apple 2
Orange 1
Orange 2
Orange 3
Kiwi 1
Kiwi 2
Kiwi 3
Kiwi 4
Banana 1
Banana 2
Banana 3
Banana 4
Breadfruit

我该怎么做?我的想法是正则表达式的组合,以检测这些不同的足够的存在并在必要时生成缺失的数字。

有问题的语言是CloverETL的CTL。使用其Normalizer组件执行一些数据清理。但是,我会接受任何语言......翻译很容易。

4 个答案:

答案 0 :(得分:1)

  

注意:此答案基于问题的旧版本

在Java中,我认为这样的事情就是你想要的:

    String[] tests = {
        "One Two 1 & 2",
        "Boeing 737 2, 4 & 6",
        "Lucky 7",
        "MI6 agent 007, 006",
        "2010-05 26, 27 & 28"
    };
    for (String test : tests) {
        String[] parts = test.split("(?=\\d+(, \\d+)*( & \\d+)?$)", 2);
        for (String number : parts[1].split("\\D+")) {
            System.out.println(parts[0] + number);
        }
    }

打印:(as seen on ideone.com

One Two 1
One Two 2
Boeing 737 2
Boeing 737 4
Boeing 737 6
Lucky 7
MI6 agent 007
MI6 agent 006
2010-05 26
2010-05 27
2010-05 28

基本上我们使用lookahead来分割特殊数字序列开始的位置,将分割限制为2个部分。然后将特殊数字序列拆分为任何非数字序列\D+

如前瞻中所示,特殊数字序列的模式是:

\d+(, \d+)*( & \d+)?$

API参考

  • String[] split(String regex, int limit)
    • limit参数控制应用模式的次数,因此会影响结果数组的长度。如果限制n大于零,那么模式将最多应用n - 1次,数组的长度将不大于n,并且数组的最后一个条目将包含所有输入超出最后匹配的分隔符。

另见


单个replaceAll解决方案

如果出于某种原因,你坚持在一个俯冲replaceAll中坚持这样做,你可以这样写:

String[] tests = {
    "One Two 1 & 2",
    "Boeing 737 2, 4 & 6",
    "Lucky 7",
    "MI6 agent 007, 006",
    "2010-05 26, 27 & 28",
};
String sequence = "\\d+(?:, \\d+)*(?: & \\d+)?$";
for (String test : tests) {         
    System.out.println(
        test.replaceAll(
            "^.*?(?=sequence)|(?<=(?=(.*?)(?=sequence))^.*)(\\d+)(\\D+)?"
                .replace("sequence", sequence),
            "$1$2$3"
        )
    );
}

输出(as seen on on ideone.com):

One Two 1 & One Two 2
Boeing 737 2, Boeing 737 4 & Boeing 737 6
Lucky 7
MI6 agent 007, MI6 agent 006
2010-05 26, 2010-05 27 & 2010-05 28

这使用了三重嵌套断言,包括Java中无限长的lookbehind feabug。我不建议使用它,但确实如此。

答案 1 :(得分:0)

最后一部分可由(?:\d+, )*\d+ & \d+$匹配。虽然您可能想用\s+替换空格。获得匹配的字符串后,将其按[,&\s]+拆分将为您提供每个数字。

实际上,如果您使用^(\D+) ((?:\d+, )*\d+ & \d+)$,匹配应返回类似[“第一部分”,“数字”]的列表。所以你得到了一切。拆分第二个字符串,然后就可以了。

答案 2 :(得分:0)

我写了Perl,因为你没有指定RegEx的哪种味道

这听起来像你想要的(假设在Foo Bar没有数字):

/(\D+)(\d+)(, \d+)*( & \d+)/;

然后$ 1将是“Foo Bar” 2美元,3美元......将是个人#s,前缀为“,”或“&amp;”,因此您需要从每个#中删除这些#。

答案 3 :(得分:0)

在CPAN上查看Parse::Range的设计:

http://cpansearch.perl.org/src/PERLER/Parse-Range-0.96/lib/Parse/Range.pm

您可能需要稍微调整逻辑以支持&符号。