如何用Java正则表达式解析骰子符号?

时间:2016-01-26 18:01:03

标签: java regex

标准RPG骰子符号是这样的:" AdB [x /] C [+ - ] D",其中A,B,C,D是自然数:A掷骰子的数量,B是每个骰子上的边数,C是跟随乘数或除数,D是加法或减法。

一些例子:

  • d6:滚动一个6面模具。
  • 3d6:每组掷3个骰子,找到总和。
  • 4d6 + 1:每组掷4个骰子,加1加总。
  • 3d6x10:每组掷3个骰子,然后将总和乘以10。
  • d6 / 2:滚动6面模具然后除以2(向上舍入)。

我喜欢一种Java方法,它以这种格式输入一个字符串,并解析出A,B,C和D的正确整数值。(例如:如果C是一个除数,那么存储一个负的int;同样如果D意味着减法也是如此。)我实际上已经实现了这一点,一些循环查看字符串中的单个字符值,但它是丑陋的罪恶。

我有直觉,通过使用正则表达式可以更简洁地解决这个问题,但坦率地说,我在这个主题中完全无知。关于如何做的任何建议?如果对问题有一个更优雅的抽象(例如,任意数量的单独的x / + - 修饰符;或者需要将x转换为*),我也会对此持开放态度。

这个问题提供了一个密切相关的正则表达式,但没有说明如何从中提取参数:Java Regex repetition (Dice notation parsing)

4 个答案:

答案 0 :(得分:4)

这个命名组示例从您的示例输入中产生了一些有希望的结果。

注意:要求Java 7用于命名的正则表达式组。

String pattern = "(?<A>\\d*)d((?<B>\\d+)(?<math>(?<mult>[x\\/](?<C>\\d+))?(?<add>[+-](?<D>\\d+))?)?)?";
Pattern p = Pattern.compile(pattern);

String[] tests = new String[] {
    "d6", "3d6", "4d6+1", "3d6x10", "d6/2", "3d4/2-7", "12d4-", "d-8", "4dx"
};

for (String test : tests) {
    System.out.printf("Testing \"%s\"\n", test);
    Matcher m = p.matcher(test);

    if (m.matches()) {
        String groupA = m.group("A");
        if (groupA == null) {
            groupA = "1"; // default one roll
        }

        String groupB = m.group("B");
        if (groupB == null) {
            groupB = "6"; // default six-sided die
        }

        String groupC = m.group("C");
        if (groupC == null) {
            groupC = "1"; // default multiply or divide by 1
        }

        String groupD = m.group("D");
        if (groupD == null) {
            groupD = "0"; // default add or subtract 0
        }

        int a = Integer.parseInt(groupA);
        int b = Integer.parseInt(groupB);
        int c = Integer.parseInt(groupC);
        int d = Integer.parseInt(groupD);

        String groupMath = m.group("math");
        if (groupMath != null && groupMath.isEmpty()) {
            groupMath = null;
        }
        String groupAdd = m.group("add");
        String groupMult = m.group("mult");

        System.out.printf("A: %d\n", a);
        System.out.printf("B: %d\n", b);
        System.out.printf("C: %d\n", c);
        System.out.printf("D: %d\n", d);
        System.out.println("------");
        System.out.printf("math: %s\n", groupMath);
        System.out.printf("mult: %s\n", groupMult);
        System.out.printf("add: %s\n", groupAdd);
    } else {
        System.out.println("No Match!");
    }

    System.out.println();
}

输出

Testing "d6"
A: 1
B: 6
C: 1
D: 0
------
math: null
mult: null
add: null

Testing "3d6"
A: 3
B: 6
C: 1
D: 0
------
math: null
mult: null
add: null

Testing "4d6+1"
A: 4
B: 6
C: 1
D: 1
------
math: +1
mult: null
add: +1

Testing "3d6x10"
A: 3
B: 6
C: 10
D: 0
------
math: x10
mult: x10
add: null

Testing "d6/2"
A: 1
B: 6
C: 2
D: 0
------
math: /2
mult: /2
add: null

Testing "3d4/2-7"
A: 3
B: 4
C: 2
D: 7
------
math: /2-7
mult: /2
add: -7

Testing "12d4-"
No Match!

Testing "d-8"
No Match!

Testing "4dx"
No Match!

答案 1 :(得分:3)

这不是最聪明或最优雅的方式,但它简短,可读且健壮。由于我不确定您打算如何使用它,因此我没有添加任何想要输出或将其转换为API /库的内容:

public class DieRegex {

    public static void main(String[] args) {

        int amount, die, mult = 1, add = 0;

        Pattern p = Pattern.compile("([1-9]\\d*)?d([1-9]\\d*)([/x][1-9]\\d*)?([+-]\\d+)?");
        Matcher m = p.matcher("d20");
        if (m.matches()) {
            amount = (m.group(1) != null) ? Integer.parseInt(m.group(1)) : 1;
            die = Integer.parseInt(m.group(2));
            if (m.group(3) != null) {
                boolean positive = m.group(3).startsWith("x");
                int val = Integer.parseInt(m.group(3).substring(1));
                mult = positive ? val : -val;
            }
            if (m.group(4) != null) {
                boolean positive = m.group(4).startsWith("+");
                int val = Integer.parseInt(m.group(4).substring(1));
                add = positive ? val : -val;
            }
        }
        else
            System.out.println("No match"); // Do whatever you need
    }
}

注意:根据有关检查输入的说明:

  • amount(A)die(B)和mult(C)必须大于0.但是,不允许使用前导零的正数(02d20无效,2d20是正确的格式)。这可以通过前瞻来解决,但会使正则表达式变得更复杂。
  • add(D)可以是任意数字(即使是前导零)。
  • mult(C)add(D)分别为1和0,如果它们不存在于字符串中。它只是一个默认值,表示&#34;没有值&#34;。
  • 检查的格式:
    • 答:[可选]非负数(默认为1)
    • d
    • B:[必需]非负数
    • C:[可选] /x后跟非负数(默认为1)
    • D:[可选] +-后跟非负数(默认为0)

答案 2 :(得分:2)

我没有对它进行广泛测试,但根据您的解释,以下代码可以正常运行。如果您注意到某些错误,可能需要对正则表达式进行一些小的调整。这个版本与其他版本的最大区别在于,它可以在一个名为RPGDice的类中进行模块化,您可以使用RPGDice.parse(expr)进行实例化,并获取包含不同属性的RPGDice实例(roll,faces,multiplier) ,添加剂)。

public class RPGDice {

    private static final Pattern DICE_PATTERN = Pattern.compile("(?<A>\\d*)d(?<B>\\d+)(?>(?<MULT>[x/])(?<C>\\d+))?(?>(?<ADD>[+-])(?<D>\\d+))?");

    private int rolls = 0;
    private int faces = 0;
    private int multiplier = 0;
    private int additive = 0;

    public RPGDice(int rolls, int faces, int multiplier, int additive) {
        this.rolls = rolls;
        this.faces = faces;
        this.multiplier = multiplier;
        this.additive = additive;
    }

    public int getRolls() {
        return rolls;
    }

    public int getFaces() {
        return faces;
    }

    public int getMultiplier() {
        return multiplier;
    }

    public int getAdditive() {
        return additive;
    }

    @Override
    public String toString() {
        return String.format("{\"rolls\": %s, \"faces\": %s, \"multiplier\": %s, \"additive\": %s}", rolls, faces, multiplier, additive);
    }

    private static boolean isEmpty(String str) {
        return str == null || str.trim().isEmpty();
    }

    private static Integer getInt(Matcher matcher, String group, int defaultValue) {
        String groupValue = matcher.group(group);
        return isEmpty(groupValue) ? defaultValue : Integer.valueOf(groupValue);
    }

    private static Integer getSign(Matcher matcher, String group, String positiveValue) {
        String groupValue = matcher.group(group);
        return isEmpty(groupValue) || groupValue.equals(positiveValue) ? 1 : -1;
    }

    public static RPGDice parse(String str) {
        Matcher matcher = DICE_PATTERN.matcher(str);
        if(matcher.matches()) {
            int rolls = getInt(matcher, "A", 1);
            int faces = getInt(matcher, "B", -1);
            int multiplier = getInt(matcher, "C", 1);
            int additive = getInt(matcher, "D", 0);
            int multiplierSign = getSign(matcher, "MULT", "x");
            int additiveSign = getSign(matcher, "ADD", "+");
            return new RPGDice(rolls, faces, multiplier * multiplierSign, additive * additiveSign);
        }
        return null;
        // OR
        // throw new IllegalArgumentException("Invalid Expression");
    }

    public static void main(String[] args) {
        System.out.println(RPGDice.parse("d6"));
        System.out.println(RPGDice.parse("d6x"));
        System.out.println(RPGDice.parse("33d6x10"));
        System.out.println(RPGDice.parse("336x10"));
        System.out.println(RPGDice.parse("d6/"));
        System.out.println(RPGDice.parse("d6/5"));
        System.out.println(RPGDice.parse("d6/5+2"));
        System.out.println(RPGDice.parse("2d6/5-32"));
        System.out.println(RPGDice.parse("2d6/5+-32"));
    }
}

输出:

{"rolls": 1, "faces": 6, "multiplier": 1, "additive": 0}
null
{"rolls": 33, "faces": 6, "multiplier": 10, "additive": 0}
null
null
{"rolls": 1, "faces": 6, "multiplier": -5, "additive": 0}
{"rolls": 1, "faces": 6, "multiplier": -5, "additive": 2}
{"rolls": 2, "faces": 6, "multiplier": -5, "additive": -32}
null

答案 3 :(得分:0)

简单回答

考虑阅读手册。 RTxM1RTxM2

TLDR

下面的代码示例的输出显示了使用正则表达式解析骰子符号时需要知道的内容。

public class RegEx
{
    private static final int EXPECTED_GROUP_COUNT = 7;

    private static final Pattern pattern = Pattern.compile("(\\d+)?[Dd](\\d+)([Xx/](\\d+))?(([+-])(\\d+))?");

    private static void matchit(final String value)
    {
        final Matcher matcher = pattern.matcher(value);

        if (matcher.matches())
        {
            final int groupCount;
            final MatchResult matchResult = matcher.toMatchResult();

            groupCount = matchResult.groupCount();

            System.out.println("kapow: " + value + ", groups: " + groupCount);

            if (groupCount == EXPECTED_GROUP_COUNT)
            {
                for (int index = 0; index <= groupCount; ++index)
                {
                    final String currentGroup = matchResult.group(index);
                    System.out.println("\tgroup[" + index + "]: " + currentGroup);
                }
            }
            else
            {
                System.out.println("match error; wrong group count");
            }
        }
        else
        {
            System.out.println("Format not recognized: " + value);
        }
    }

    public static void main(
        String[] args)
    {
        final String[] thingArray =
        {
            "3d6",
            "d7",
            "4D6+4",
            "3d6x10",
            "d6/2"
        };

        for (final String thing : thingArray)
        {
            matchit(thing);
        }
    }
}