Home > front end >  Regex Grabbing Values after specific character
Regex Grabbing Values after specific character

Time:02-01

I have the following lines:

G1 X70.128 Y69.369 E11.35622
G1 X70.128 Y69.369 Z12 F12
G1 Z20

and all combinations there of.

For a parser i need a specfic regex that can return met values after each speficic denimoniator.

So with one regex i would like to be able to grab X,Y,Z and E from a specific input line.

is this possible?

I was thinking of something like this:

([Gg]0?[01]) *(([Xx]) *(-?\d .?\d*)) *(([Yy]) *(-?\d .?\d*))? *(([Zz]) *(-?\d .?\d*))? *(([eE]) *(-?\d .?\d*))? *(([fF]) *(-?\d .?\d*))?

However this dictates that the order of the Letters are always as follows:

X, Y, Z, E, F

I would like that the order of the the parameters can be any. But the group of the result for example X. is always 1. and that Z is always 3.

Even when the input is: Z12 X13 for example.

Edit.

The input The fourth bird. works great in the demo but when using it in Java/Kotlin im getting 0 matches.

val movePattern = Pattern.compile("""\b(?:[Xx](\d (?:\.\d )?)|[Yy](\d (?:\.\d )?)|[Zz](\d (?:\.\d )?)|[Ee](\d (?:\.\d )?)|[Ff](\d (?:\.\d )?))\b""")

val matches = movePattern.matcher("G1 Z0.350 F7800.000")

CodePudding user response:

You can use

(?i)\b(?:x(\d*\.?\d )|y(\d*\.?\d )|z(\d*\.?\d )|e(\d*\.?\d )|f(\d*\.?\d ))\b

See the regex demo. Details:

  • (?i) - case insensitive inline modifier
  • \b - a word boundary
  • (?: - start of a non-capturing group:
    • x(\d*\.?\d )| - x and an int or float value captured into Group 1, or
    • y(\d*\.?\d )| - y and an int or float value captured into Group 2, or
    • z(\d*\.?\d )| - z and an int or float value captured into Group 3, or
    • e(\d*\.?\d )| - e and an int or float value captured into Group 4, or
    • f(\d*\.?\d ) - f and an int or float value captured into Group 5
  • ) - end of the group
  • \b - a word boundary.

In Kotlin, you need to use the pattern with .findAll:

fun main(args: Array<String>) {
    val regex = """(?i)\b
        |(?:x(\d*\.?\d )|
        |y(\d*\.?\d )|
        |z(\d*\.?\d )|
        |e(\d*\.?\d )|
        |f(\d*\.?\d ))\b
    """.trimMargin().toRegex(RegexOption.COMMENTS)
    val matches = regex.findAll("G1 Z0.350 F7800.000").map { it.destructured.toList() }
    matches.forEach { System.out.print(it) }
    // => [, , 0.350, , ][, , , , 7800.000]
}

See the online demo

CodePudding user response:

Instead of matching the occurrences in a fixed order, you can use an alternation | with a case insensitive match.

To get the digits only in any order, you can match the leading character, and capture the digits in a capture group. The order of the capture groups is from left to right.

To prevent partial word matches, you can use word boundaries \b any of the matches by placing them in a non capture group.

\b(?:X(\d (?:\.\d )?)|Y(\d (?:\.\d )?)|Z(\d (?:\.\d )?)|E(\d (?:\.\d )?))\b
  • \b A word boundary to prevent a partial word match
  • (?: Non capture group
    • X(\d (?:\.\d )?) Match X and capture in group 1 1 digits and with an optional decimal part
    • | Or
    • Y(\d (?:\.\d )?) Capture group 2 for the Y char
    • | Or
    • Z(\d (?:\.\d )?) Capture group 3 for the Z char
    • | Or
    • E(\d (?:\.\d )?) Capture group 4 for the E char
  • ) Close the non capture group
  • \b A word boundary

See a regex demo and a Java demo

In Java you can loop all matches using .find() and a while loop:

String regex = "\\b(?:X(\\d (?:\\.\\d )?)|Y(\\d (?:\\.\\d )?)|Z(\\d (?:\\.\\d )?)|E(\\d (?:\\.\\d )?))\\b";
String string = "G1 X70.128 Y69.369 e11.35622\n"
          "G1 Z20";

Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
Matcher matcher = pattern.matcher(string);

while (matcher.find()) {
    if (null != matcher.group(1)) System.out.println(matcher.group(1));
    if (null != matcher.group(2)) System.out.println(matcher.group(2));
}

Output

70.128
69.369

etc..

  •  Tags:  
  • Related