Lets say I have a string such as:
155 44x3/2-12
How can I split it up so that the end result would be:
$numbers = [155, 44, 3, 2, 12];
$operators = [' ', 'x', '/', '-'];
I can get one or the other separately fairly easily using regex, but is there a good way to get the numbers using regex and then get "the rest" into another variable without writing explicit regex for it? The array items should be correctly ordered as per the initial string.
Or two separate regex clauses would be the best in this case? E.g.
preg_match_all($string, "/(\d )/", $numbers);
preg_match_all($string, "/(\D )/", $rest);
CodePudding user response:
You may be able to do this in a single preg_match_all call with 2 capture groups in this regex:
(\d )(\D*)
Which matches 1 digits in capture group #1 and matches 0 or more non-digits in capture group #2
$s = '155 44x3/2-12';
if (preg_match_all('/(\d )(\D*)/', $s, $m)) {
$numbers = $m[1];
$operators = array_filter($m[2]);
// print arrays
print_r($numbers);
print_r($operators);
}
Output:
Array
(
[0] => 155
[1] => 44
[2] => 3
[3] => 2
[4] => 12
)
Array
(
[0] =>
[1] => x
[2] => /
[3] => -
)
array_filter has been used to filter out empty elements from array.
CodePudding user response:
Using two regexes is no sin to be sure, and the preg_match_all() of the other answer would also work. Another approach is to capture them into a single array, and then fork it into two. Since we're talking about splitting, let's use preg_split() with the PREG_SPLIT_DELIM_CAPTURE flag. That produces a single array of numbers, as below for your sample equation:
Array [
[0] => 155
[1] =>
[2] => 44
[3] => x
[4] => 3
[5] => /
[6] => 2
[7] => -
[8] => 12
]
Now, since your equation will start with a number, it's a fairly safe assumption that all your operators will have odd keys in the resulting array. We can then separate them with % 2 == 0, or zero modulo with two, on the key, thereby forking odds and evens into separate arrays.
$equation = '155 44x3/2-12';
// Use ~\s*(\D)\s*~ if you may have spaces around operators
// Use ~(?<!\D)(\D)~ with lookbehind if you have negative integers
$splits = preg_split('~(\D)~', $equation, -1, PREG_SPLIT_DELIM_CAPTURE);
$bits = [];
array_walk($splits, function($v, $k) use (&$bits) {
$type = $k % 2 == 0 ? 'numbers' : 'operators';
$bits[$type][] = $v;
});
This results in:
array(2) {
["numbers"] · array(5) {
[0] · string(3) "155"
[1] · string(2) "44"
[2] · string(1) "3"
[3] · string(1) "2"
[4] · string(2) "12"
}
["operators"] · array(4) {
[0] · string(1) " "
[1] · string(1) "x"
[2] · string(1) "/"
[3] · string(1) "-"
}
}
Be aware that a basic regex separating digits from operators will fail if you have - signed integers in there. You can add in a negative look-behind that excludes a minus preceded by an operator; and your regex would then look like ~(?<!\D)(\D)~ instead.
You might also have spaces around the operators. In that case, add optional spaces outside the operator capture/split-matcher regex: ~\s*(\D)\s*~ to have spaces discarded in the split while the operator itself is captured.
N.B. You can't combine the negative lookbehind for - signed numbers with an optional space, because negative lookbehinds need to be fixed-length. You either have spaces (?<!\D\s) or you don't (?<!\D). This will work: ~\s*(?<!\D\s)(\D)\s*~ for 155 44 x 3 / 2 * -12; and this ~\s*(?<!\D)(\D)\s*~ for splitting 155 44x3/2*-12.
Further, if your equation uses two-character-operators, like PHP's ** for power-of, you'll want to revise the regex to match. If you have ( brackets ) and other notation included, it's a whole other horse to ride again. This approach should do for the basics though.
