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, ory(\d*\.?\d )|
-y
and an int or float value captured into Group 2, orz(\d*\.?\d )|
-z
and an int or float value captured into Group 3, ore(\d*\.?\d )|
-e
and an int or float value captured into Group 4, orf(\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 groupX(\d (?:\.\d )?)
MatchX
and capture in group 1 1 digits and with an optional decimal part|
OrY(\d (?:\.\d )?)
Capture group 2 for theY
char|
OrZ(\d (?:\.\d )?)
Capture group 3 for theZ
char|
OrE(\d (?:\.\d )?)
Capture group 4 for theE
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..