【问题标题】:How to pipeline/chained regex expressions如何管道/链接正则表达式
【发布时间】:2016-02-14 21:56:36
【问题描述】:

我必须从字符串文本中提取度量单位和数量。就像在这个示例中一样:

Original String // result:

abc1mgabc // extract 1 and mg separately
abc100mlabc //100 and ml
abc256kgabc //256 and kg

直到现在我第一次使用这个正则表达式:

(?i)\d{1,5}(mg|g|gr|kg|ml|l)

提取数量和单位并存储到quant_unit字符串中。

之后,我应用quant_unit 这两个正则表达式:\d(?i)(mg|g|gr|kg|ml|l) 分别提取“数量”和“测量单位”。

但我认为它必须有一种方法可以通过在原始字符串上仅应用正则表达式(对于要提取的每个项目)来单独提取它?

我虽然使用了一些类似的东西:

original_string -> applyRegex(提取度量和单位)-> applyRegex2(从中提取度量或单位)。

使用它自己的正则表达式或 Java 中的 Pattern 类。

我正在创建一个枚举来轻松访问模式:

public enum Patterns {

    //the expression is: (?i)\d{1,5}(mg|g|gr|kg|ml|l)
    QUANTITY_UNIT("(?i)\\d{1,5}(" + MeasureUnit.getRegex() + ")"),
    QUANTITY("\\d"),
    UNIT("(?i)(" + MeasureUnit.getRegex() + ")");

    private Pattern pattern;

    Patterns(String patternString) {
        System.out.println(patternString);
        pattern = pattern.compile(patternString);
    }

    public Pattern getPattern() {
        return pattern;
    }

    public Matcher getMatcher(CharSequence input) {
        return getPattern().matcher(input);
    }

    public String findGroup(CharSequence input) {
        Matcher matcher = getMatcher(input);
        matcher.find();
        return matcher.group();

    }

以及所需行为的单元测试:

public class PatternsTest {

@Test
public void quantityUnit() {
    String testString = "abc1kgabc1l";
    String fg = Patterns.QUANTITY_UNIT.findGroup(testString);
    Assert.assertEquals("1KG", fg);
}


@Test
public void quantity() {
    String testString = "abc1kgabc1l";
    String fg = Patterns.QUANTITY.findGroup(testString);
    Assert.assertEquals("1", fg);
}


@Test
public void unity() {
    String testString = "abc1kgabc1l";
    String fg = Patterns.UNIT.findGroup(testString);
    Assert.assertEquals("kg", fg);
}

}

编辑

我根据评论和答案做了一些重构,现在可以正常工作了:

public enum Patterns {

    QUANTITY_UNIT("(?i)([0-9]+)(" + MeasureUnit.getRegex() + ")");

    private Pattern pattern;

    Patterns(String patternString) {
        pattern = pattern.compile(patternString);
    }

    public Pattern getPattern() {
        return pattern;
    }

    public Matcher getMatcher(CharSequence input) {
        return getPattern().matcher(input);
    }

    public String getQuantity(CharSequence input) {
        final int group_idx = 1;

        Matcher matcher = getMatcher(input);
        boolean found = matcher.find();
        return found ? toLower(matcher.group(group_idx)) : "";
    }

    private String toLower(String input) {
        return input.toLowerCase();
    }

    public String getUnity(CharSequence input) {
        final int group_idx = 2;

        Matcher matcher = getMatcher(input);
        boolean found = matcher.find();
        return found ? toLower(matcher.group(group_idx)) : "";
    }
}

测试:

public class MeasureUnityTest {


    @Test
    public void quantity() {
        String testString = "abc1kgabc1l";
        String fg = QUANTITY_UNIT.getQuantity(testString);
        Assert.assertEquals("1", fg);
    }


    @Test
    public void unity() {
        String testString = "abc1kgabc1l";
        String fg = QUANTITY_UNIT.getUnity(testString);
        Assert.assertEquals("kg", fg);
    }

    @Test
    public void unityUpperCase() {
        String testString = "abc1KGabc1l";
        String fg = QUANTITY_UNIT.getUnity(testString);
        Assert.assertEquals("kg", fg);
    }


    @Test
    public void unityNoOccurrence() {
        String testString = "fasfasfasfaf";
        String fg = QUANTITY_UNIT.getQuantity(testString);
        Assert.assertEquals("", fg);
    }

    @Test
    public void unityEmptyString() {
        String testString = "";
        String fg = QUANTITY_UNIT.getQuantity(testString);
        Assert.assertEquals("", fg);
    }

    /* If more than one matches, return the first*/
    @Test
    public void unityMoreThanOne() {
        String testString = "abc5mlabc5kg";
        String fg = QUANTITY_UNIT.getUnity(testString);
        Assert.assertEquals("ml", fg);
    }

    /* If more than one matches, return the first*/
    @Test
    public void quantityMoreThanOne() {
        String testString = "abcm5mlabc1kg";
        String fg = QUANTITY_UNIT.getQuantity(testString);
        Assert.assertEquals("5", fg);
    }

}

【问题讨论】:

    标签: java regex pattern-matching


    【解决方案1】:

    概括所有的cmets,你可以使用类似这样的东西(IDEONE link):

    String[] tests = { "abc1mgabc","abc100mlabc","abc256kgabc"};
    Pattern ptrn = Pattern.compile("(?i)([0-9]+)(gr|kg|mg|ml|g|l)");
    for (String s: tests) {
        Matcher matcher = ptrn.matcher(s);
        while (matcher.find()) {
            System.out.println("QNTY: " + matcher.group(1));
            System.out.println("UNIT:" + matcher.group(2));
        }
    }
    

    输出:

    QNTY: 1
    UNIT:mg
    QNTY: 100
    UNIT:ml
    QNTY: 256
    UNIT:kg
    

    IDEONE demo

    要点:

    • 您可以只使用 2 个捕获组并使用 1 个正则表达式捕获两个实体。
    • 确保最长的备选方案在交替中排在第一位,因为在 Java 正则表达式中选择了找到的第一个备选方案(不符合 POSIX 标准的备选方案,即选择找到的最长备选方案)。
    • 如果您不确定数字有多大,使用 + 量词(出现一次或多次)就足够了,而不是限制性的 {1,5} 只会匹配 1 到 5 次出现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-03
      • 2022-07-08
      • 1970-01-01
      相关资源
      最近更新 更多