How to split math operators from numbers in a string - php

Im trying to split between operators and float/int values in a string.
Example:
$input = ">=2.54";
Output should be:
array(0=>">=",1=>"2.54"); .
Operators cases : >,>=,<,<=,=
I tried something like this:
$input = '0.2>';
$exploded = preg_split('/[0-9]+\./', $input);
but its not working.

Here is a working version using preg_split:
$input = ">=2.54";
$parts = preg_split("/(?<=[\d.])(?=[^\d.])|(?<=[^\d.])(?=[\d.])/", $input);
print_r($parts);
This prints:
Array
(
[0] => >=
[1] => 2.54
)
Here is an explanation of the regex used, which says to split when:
(?<=[\d.])(?=[^\d.]) a digit/dot precedes and a non digit/dot follows
| OR
(?<=[^\d.])(?=[\d.]) a non digit/dot precedes and a digit/dot follows
That is, we split at the interface between a number, possibly a decimal, and an arithmetic symbol.

Try :
$input = ">=2.54";
preg_match("/([<>]?=?) ?(\d*(?:\.\d+)?)/",$input,$exploded);

If you want to split between the operators, you might use and alternation to match the variations of the operators, and use \K to reset the starting point of the reported match.
This will give you the position to split on. Then assert using lookarounds that there is a digit on the left or on the right.
\d\K(?=[<>=])|(?:>=?|<=?|=)\K(?=\d)
Explanation
\d\K(?=[<>=]) Match a digit, forget what was matched and assert either <, > or = on the right
| Or
(?:>=?|<=?|=)\K(?=\d) Match an operator, forget what was matched and assert a digit on the right
Regex demo | Php demo
For example
$strings = [
">=2.54",
"=5",
"0.2>"
];
$pattern = '/\d\K(?=[<>=])|(?:>=?|<=?|=)\K(?=\d)/';
foreach ($strings as $string) {
print_r(preg_split($pattern, $string));
}
Output
Array
(
[0] => >=
[1] => 2.54
)
Array
(
[0] => =
[1] => 5
)
Array
(
[0] => 0.2
[1] => >
)

Related

Explode a string where the explode condition is bunch of specific characters

I'm looking for a way to explode a string. For example, I have the following string: (we don't count the beginning - 0x)
0xa9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d00000000000000000000000000000000000000000000000000000000000054368
which is actually an ETH transaction input. I need to explode this string into 3 parts. Imagine 1 bunch of zeros is actually a single space and these spaces define the gates where the string should be exploded.
How can I do that?
preg_split()
This function uses a regular expression to split a string.
So in this example at two or more 0 in a row:
$arr = preg_split('/[0]{2,}/', $string);
print_r($arr);
echo PHP_EOL;
This will output the following:
Array
(
[0] => a9059xbb
[1] => fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d
[2] => 54368
)
Be aware that you will have problems if a message itself has a 00 in it. Assuming it is used as a null-byte for "end of string", this will not happen, though.
preg_match()
This is an example using regular expressions. You can split at arbitrary points.
$string = 'a9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d00000000000000000000000000000000000000000000000000000000000054368';
print_r($string);
echo PHP_EOL;
$res = preg_match('/(.{4})(.{32})(.{32})/', $string, $matches);
print_r($matches);
echo PHP_EOL;
This outputs:
a9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d00000000000000000000000000000000000000000000000000000000000054368
Array
(
[0] => a9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199a
[1] => a905
[2] => 9xbb000000000000000000000000fc7a
[3] => 5f48a1a1b3f48e7dcb1f23a1ea24199a
)
As you can see /(.{4})(.{32})(.{32})/ will find 4 bytes, then 32 and after that 32 again. Capturing groups are made with () around what you want to find. They appear in the $matches array (0 is always the whole string found).
In case you want to ignore certain parts you can express that as well:
/(.{4})9x(.{32}).{4}(.{32})/
This changes the found string:
Array
(
[0] => a9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d000
[1] => a905
[2] => bb000000000000000000000000fc7a5f
[3] => a1b3f48e7dcb1f23a1ea24199af4d000
)
Links
PHP documentation for the mentioned functions:
https://www.php.net/manual/en/function.preg-split.php
https://www.php.net/manual/en/book.pcre.php
Play around with the second regular expression using this demo:
https://regex101.com/r/pfZtH8/1
If you will always explode them at the same points (4 bytes(8 hexadecimal digits), 32 bytes(64 hexadecimal digits), 32 bytes(64 hexadecimal digits)), you could use substr().
$input = "0xa9059xbb000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d00000000000000000000000000000000000000000000000000000000000054368";
$first = substr($input,2,8);
$second = substr($input,10,64);
$third = substr($input,74,64);
print_r($first);
print "<br>";
print_r($second);
print "<br>";
print_r($third);
print "<br>";
this outputs:
a9059xbb
000000000000000000000000fc7a5f48a1a1b3f48e7dcb1f23a1ea24199af4d0
0000000000000000000000000000000000000000000000000000000000054368

Regex formula spliting with preg_split

Hello i am passing a bad moment trying to found the correct Regex formula.
$stringSplit = "+foo a -ba24+Sample3";
$vectorsPlus = preg_split('/[+*]/',$stringSplit ,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
$vectorsMinus = preg_split('/[-*]/',$stringSplit ,-1,PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
My objetive it's get 2 diferents arrays grouping on symbol like:
$vectorsPlus [0] = '+foo';
$vectorsPlus [1] = '+Sample3';
$vectorsMinus [0] = '-ba24';
I will aprecciate any help how to solves this with the Regex magic.
Instead of using split, you can match the values.
Note that [+*] and [-*] both match a * char and which is not in the example data.
You can match either a + or a - followed by matching the opposite excluding whitespace chars using a negated character class.
$stringSplit = "+foo a -ba24+Sample3";
preg_match_all("/\+[^-+\s]+/", $stringSplit, $matchesPlus);
print_r($matchesPlus[0]);
preg_match_all("/-[^-+\s]+/", $stringSplit, $matchesMinus);
print_r($matchesMinus[0]);
Output
Array
(
[0] => +foo
[1] => +Sample3
)
Array
(
[0] => -ba24
)
See a php demo

Using regex to not match periods between numbers

I have a regex code that splits strings between [.!?], and it works, but I'm trying to add something else to the regex code. I'm trying to make it so that it doesn't match [.] that's between numbers. Is that possible? So, like the example below:
$input = "one.two!three?4.000.";
$inputX = preg_split("~(?>[.!?]+)\K(?!$)~", $input);
print_r($inputX);
Result:
Array ( [0] => one. [1] => two! [2] => three? [3] => 4. [4] => 000. )
Need Result:
Array ( [0] => one. [1] => two! [2] => three? [3] => 4.000. )
You should be able to split on this:
(?<=(?<!\d(?=[.!?]+\d))[.!?])(?![.!?]|$)
https://regex101.com/r/kQ6zO4/1
It uses lookarounds to determine where to split. It looks behind to try to match anything in the set [.!?] one or more times as long as it isn't preceded by and succeeded by a digit.
It also won't return the last empty match by ensuring the last set isn't the end of the string.
UPDATE:
This should be much more efficient actually:
(?!\d+\.\d+).+?[.!?]+\K(?!$)
https://regex101.com/r/eN7rS8/1
Here is another possibility using regex flags:
$input = "one.two!three???4.000.";
$inputX = preg_split("~(\d+\.\d+[.!?]+|.*?[.!?]+)~", $input, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
print_r($inputX);
It includes the delimiter in the split and ignores empty matches. The regex can be simplified to ((?:\d+\.\d+|.*?)[.!?]+), but I think what is in the code sample above is more efficient.

How to split a string on logic operators

I'm needing to parse some user inputs. They're coming to me in the form of clauses ex:
total>=100
name="foo"
bar!="baz"
I have a list of all of the available operators (<, >, <=, !=, = etc) and was using this to build a regex pattern.
My goal is to get each clause split into 3 pieces:
$result=["total", ">=", "100"]
$result=["name", "=", "foo"]
$result=["bar", "!=", "baz"]
My pattern takes all the operators and builds something like this (condensed for length)(this example only matches > and >=:
preg_split("/(?<=>)|(?=>)|(?<=>=)|(?=>=)/", $clause,3)
So a lookbehind and a lookahead for each operator. I had preg_split restrict to 3 groups in case a string contained an operator character (name="<wow>").
My regex works pretty great, however it fails terribly for any operator which includes characters in another operator. For example, >= is never split right because > is matched and split first. The same for != which is matched by =
Here's what I'm getting:
$result=["total", ">", "=100"]
$result=["bar", "!", "=baz"]
Is it possible to use regex to do what I'm attempting? I need to keep track of the operator and can't simply split the string on it (hence the lookahead/behind solution).
One possiblity I considered would be to force a space or unusual character around all the operators so that > and >= would become, say, {>} and {>=} if the regex had to match the brackets, then it wouldn't be able to match early like it is now. However, this isn't an elegant solution and it seems like some of the regex masters here might know a better way.
Is regex the best solution or should I use string functions?
This question is somewhat similar, but I don't believe the answer's pseudocode is accurate - I couldn't get it to work well. How to manipulate and validate string containing conditions that will be evaluated by php
I'd suggest matching instead of splitting, as the result will still be an array.
^(.*?)([!<>=|]=?)(.*?)$
Here is a demo.
PHP code:
$re = "/^(.*?)([!<>=|]=?)(.*?)$/m";
$str = "total>=100\nname=\"foo\"\nbar!=\"baz\"";
preg_match_all($re, $str, $matches);
print_r($matches);
Output:
Array
(
[0] => Array
(
[0] => total>=100
[1] => name="foo"
[2] => bar!="baz"
)
[1] => Array
(
[0] => total
[1] => name
[2] => bar
)
[2] => Array
(
[0] => >=
[1] => =
[2] => !=
)
[3] => Array
(
[0] => 100
[1] => "foo"
[2] => "baz"
)
)
You can try this regexp
/^(.*)([><!]?[=]+|[>]+|[<]+)(.*)$/mgU
I have tried it here: https://regex101.com/ with input:
xxx>"sdads"
yyy<"sadasd"
name="foo"
total>=100
total<=100
total<=100
bar!="baz"
and it matched everything in right place
Using the regex: /([^<=>!]*)([<=>!]{1,2})(.*)/ with preg_match on each line will get you the desired result; at least for your examples, but likely much more.
I think one syntax that is useful and maybe you didn't know about is [].
[...] means match any character in the braces
[^...] means match any character NOT in the braces
Code example
$test = 'total>=100';
$regex = '/([^<=>!]*)([<=>!]{1,2})(.*)/';
preg_match($regex, $test, $match);
print_r($match);
result:
array(4
0 => total>=100
1 => total
2 => >=
3 => 100
)

How to split a string on comma that is NOT followed by a space?

I want the results to be:
Cats, Felines & Cougars
Dogs
Snakes
This is the closest I can get.
$string = "Cats, Felines & Cougars,Dogs,Snakes";
$result = split(',[^ ]', $string);
print_r($result);
Which results in
Array
(
[0] => Cats, Felines & Cougars
[1] => ogs
[2] => nakes
)
You can use a negative lookahead to achieve this:
,(?!\s)
In simple English, the above regex says match all commas only if it is not followed by a space (\s).
In PHP, you can use it with preg_split(), like so:
$string = "Cats, Felines & Cougars,Dogs,Snakes";
$result = preg_split('/,(?!\s)/', $string);
print_r($result);
Output:
Array
(
[0] => Cats, Felines & Cougars
[1] => Dogs
[2] => Snakes
)
the split() function has been deprecated so I'm using preg_split instead.
Here's what you want:
$string = "Cats, Felines & Cougars,Dogs,Snakes";
$result = preg_split('/,(?! )/', $string);
print_r($result);
This uses ?! to signify that we want split on a comma only when not followed by the grouped sequence.
I linked the Perl documentation on the operator since preg_split uses Perl regular expressions:
http://perldoc.perl.org/perlre.html#Look-Around-Assertions
If you want to split by a char, but want to ignore that char in case it is escaped, use a lookbehind assertion.
In this example a string will be split by ":" but "\:" will be ignored:
<?php
$string='a:b:c\:d';
$array=preg_split('#(?<!\\\)\:#',$string);
print_r($array);
?>
Results into:
Array
(
[0] => a
[1] => b
[2] => c\:d
)
http://www.php.net//manual/en/function.preg-split.php

Categories