RegexCheck if "|" is in a string - php

I want to check if a string has the character "|" (only one time and not at the beginning or end of the string). If this is the case the string should be splitted. The second check is if the first part is "X" or not.
Example:
$string = "This|is an example"; // Output: $var1 = "This"; $var2 = "is an example";
RegEx is really difficult for me. This is my poor attempt:
if (preg_match('/(.*?)\|(.*?)/', $string, $m)) {
$var1 = $m[1];
$var2 = $m[2];
if ($var1 == "X") // do anything
else // Do something else

A pure regex solution would be:
^ -- start of input
[^|]+ -- some non-pipes
\| -- a pipe
[^|]+ -- some non-pipes
$ -- finita la comedia
However, string functions might work better in this case, since you're going to split it anyways:
$x = explode('|', $input);
if(count($x) == 2 && strlen($x[0]) && strlen($x[1]))
// all right

If you don't know regex you might want a solution which doesn't use regex.
$test = ["|sdf", "asd|asad", "asd|", "asdf", "sd|sdf|sd"];
foreach ($test as $string) {
$res = explode("|", $string);
if (2 === count($res) && strlen($res[0]) && strlen($res[1])) {
var_dump($res);
}
}
Result:
array(2) {
[0]=>
string(3) "asd"
[1]=>
string(4) "asad"
}

Related

eval() alternative to get output of the string like "(0 AND 1 OR 1)"

I have logical expression data from the database in string format -
"(id1 OR id2) AND (id3 OR id3)"
This can be any type of combination as it is a dynamic string based on user input.
After getting this from DB, I replace all Ids value by 0/1 based on some logic and this sting becomes -
"(1 OR 1) AND (0 OR 1)"
Now I want to run this string as PHP code and expect its output as a Boolean. But the problem is I used eval() PHP function which is not good for security reasons.
$op = "(1 OR 1) AND (0 OR 1)";
$op = eval('return '.$op.';');
And the only reason, I had to use eval is string type. If I run this echo (1 OR 1) AND (0 OR 1) then I get the expected output.
So can someone help me with this? What can be a better way for this?
Singular format
If it's always in the same format you could use a regex and format your own expression...
if(preg_match("/\((\d) OR (\d)\) AND \((\d) OR (\d)\)/", $string, $matches))
var_dump( ($matches[1] || $matches[2]) && ($matches[3] || $matches[4]));
Multiple format
Warning The below doesn't catch every expression if it isn't correctly formatted. For example the expression:
AND 1
Would result in a failure for both methods... You could get around this in regex by defining additional matching rules, for example:
Pattern Replacement
/^\s*AND\s*1/ 0
/^\s*AND\s*0/ 0
/^\s*OR\s*0/ 0
/^\s*OR\s*1/ 1
But to cover everything would mean a long list of rules.
Example expressions
$expressions = [
"1 AND 0",
"1 AND 1",
"0 OR 0",
"0 OR 1",
"1 OR 1",
"0 AND (1 OR 1) AND (0 OR 0) OR 0",
"(1 OR 1) AND (0 OR 1)",
"(1 AND 0 OR 1 ) OR (0 OR 1 OR 1) AND 1",
"(1 OR 0) DELETE EVERYTHING!"
];
Method 1: eval
While people tend to hate on the use of eval (because it potentially allows the execution of rogue code) there's no reason not to use it so long as you sanitize your inputs.
So lets make a simple function:
function cleanBooleanExpression($string){
$string = preg_replace("/OR/i", "O", $string); // Replace instances of OR with the letter O; i flag makes it case insensitive
$string = preg_replace("/AND/i", "A", $string); // Replace instances of AND with the letter A; i flag makes it case insensitive
$string = preg_replace("/[^OA01() ]/", "", $string); // Strip all non-legal characters
$string = preg_replace("/O/", "OR", $string); // Re-instate OR
$string = preg_replace("/A/", "AND", $string); // Re-instate AND
return $string; // Return the updated string
}
You can then use the sanitized output in eval:
foreach($expressions as $input){
$input = cleanBooleanExpression($input);
eval("var_dump({$input});");
}
/* Output:
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)
*/
Method 2: custom function
Alternatively, if you really don't want to use eval, you could use regular expressions to evaluate the input:
function evalBooleanString($string){
$string = preg_replace('/
(
\(1\)|
[01]*1[01]+|
[01]+1[01]*|
\(*\s*1\s*OR\s*[01]\s*\)*|
\(*\s*[01]\s*OR\s*1\s*\)*|
\(*\s*1\s*AND\s*1\s*\)*
)
/x', 1, $string);
$string = preg_replace('/
(
\(\)|
\(0\)|
00+|
\(*\s*0\s*OR\s*0\s*\)*|
\(*\s*[01]\s*AND\s*0\s*\)*|
\(*\s*0\s*AND\s*[01]\s*\)*
)
/x', 0, $string);
$string = trim($string);
if($string === "1" || $string === "0"){
return (bool)$string;
}
return evalBooleanString($string);
}
And use it like:
foreach($expressions as $input){
$input = cleanBooleanExpression($input);
var_dump(evalBooleanString($input));
}
/* Output:
bool(false)
bool(true)
bool(false)
bool(true)
bool(true)
bool(false)
bool(true)
bool(true)
bool(true)
*/
Maybe help
function tmp_function($body)
{
$tmp_file = tempnam(sys_get_temp_dir(), "tmp_func");
file_put_contents($tmp_file, "<?php return function() { return $body };");
$function = include($tmp_file);
unlink($tmp_file);
return $function;
}
$op = "(1 OR 1) AND (0 OR 1);";
$call_me = tmp_function($op);
var_dump($call_me());

php regex to extract single parameter value from string

I'm working with a string containing parameters, separated by some special characters in PHP with preg_match
An example could be like this one, which has four parameters.
1stparm?#?1111?#?2ndParm?#?2222?#?3rdParm?#?3333?#?4thparm?#?444?#?
Each parameter name is followed by ?#?, and its value is right next to it, ending with ?#? (note: values can be strings or numbers, and even special characters)
I've probably overcomplicated my regex, which works in SOME cases, but not if I search for the last parameter in the string..
This example returns 2222 as the correct value (in group 1) for 2ndParm
(?:.*)2ndParm\?#\?(.*?)\?#\?(?=.)(.*)
but it fails if 2ndParm is the last one in the string as in the following example:
1stparm?#?1111?#?2ndParm?#?2222?#?
I'd also appreciate help in just returning one group with my result.. i havent been able to do so, but since I always get the one I'm interested in group 1, I can get it easily anyway.
Without regex:
$str ='1stparm?#?1111?#?2ndParm?#?2222?#?3rdParm?#?3333?#?4thparm?#?444?#?';
$keyval = explode('?#?', trim($str, '?#'));
$result = [];
foreach($keyval as $item) {
[$key, $result[$key]] = explode('?#?', $item);
}
print_r($result);
demo
You don't need to use a regex for everything, and you should have a serious talk with whoever invented this horrid format about the fact that JSON, YAML, TOML, XML, etc exist.
function bizarre_unserialize($in) {
$tmp = explode('?#?', $in);
$tmp = array_filter($tmp); // remove empty
$tmp = array_map(
function($a) { return explode('?#?', $a); },
$tmp
);
// rearrange to key-value
return array_combine(array_column($tmp, 0), array_column($tmp, 1));
}
$input = '1stparm?#?1111?#?2ndParm?#?2222?#?3rdParm?#?3333?#?4thparm?#?444?#?';
var_dump(
bizarre_unserialize($input)
);
Output:
array(4) {
["1stparm"]=>
string(4) "1111"
["2ndParm"]=>
string(4) "2222"
["3rdParm"]=>
string(4) "3333"
["4thparm"]=>
string(3) "444"
}
You can use
(?P<key>.+?)
\Q?#?\E
(?P<value>.+?)
\Q?#?\E
in verbose mode, see a demo on regex101.com.
The \Q...\E construct disables the ? and # "super-powers" (no need to escape them here).
In PHP this could be
<?php
$string = "1stparm?#?1111?#?2ndParm?#?2222?#?3rdParm?#?3333?#?4thparm?#?444?#?";
$regex = "~(?P<key>.+?)\Q?#?\E(?P<value>.+?)\Q?#?\E~";
preg_match_all($regex, $string, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
echo $match["key"] . " = " . $match["value"] . "\n";
}
?>
Which yields
1stparm = 1111
2ndParm = 2222
3rdParm = 3333
4thparm = 444
Or shorter:
$result = array_map(
function($x) {return array($x["key"] => $x["value"]);}, $matches);
print_r($result);

How can I match just the first line of occurrence?

I have this string:
$str = "11ff11
22mm22
33gg33
mm22mm
vv55vv
77ll77
55kk55
kk22kk
bb11bb";
There is two kind of patterns:
{two numbers}{two letters}{two numbers}
{two letters}{two numbers}{two letters}
I'm trying to match the first line when pattern changes. So I want to match these:
11ff11 -- this
22mm22
33gg33
mm22mm -- this
vv55vv
77ll77 -- this
55kk55
kk22kk -- this
bb11bb
Here is my current pattern:
/(\d{2}[a-z]{2}\d{2})|([a-z]{2}\d{2}[a-z]{2})/
But it matches all lines ..! How can I limit it to match just first line of same pattern?
I could not do it with lookaround due to the problem with spaces. But with classic regex it's available. It finds sequences of repeating pattern and capture only he first one
(?:(\d{2}[a-z]{2}\d{2})\s+)(?:\d{2}[a-z]{2}\d{2}\s+)*|(?:([a-z]{2}\d{2}[a-z]{2})\s+)(?:[a-z]{2}\d{2}[a-z]{2}\s+)*
demo and some explanation
To understand how it works i made simple exmple with patterns of digit and letter:
(?:(\d)\s+)(?:\d\s+)*|(?:(a)\s+)(?:a\s+)*
demo and some explanation
Not sure if you can do this with only one expression, but you can iterate over your string and test when changes:
<?php
$str = "11ff11
22mm22
33gg33
mm22mm
vv55vv
77ll77
55kk55
kk22kk
bb11bb";
$exploded = explode(PHP_EOL, $str);
$patternA = '/(\d{2}[a-z]{2}\d{2})/';
$patternB = '/([a-z]{2}\d{2}[a-z]{2})/';
$result = [];
$currentPattern = '';
//get first and check what pattern is
if(preg_match($patternA, $exploded[0])){
$currentPattern = $patternA;
$result[] = $exploded[0];
} elseif(preg_match($patternB, $exploded[0])){
$currentPattern = $patternB;
$result[] = $exploded[0];
} else {
//.. no pattern on first element, should we continue?
}
//toggle
$currentPattern = $currentPattern == $patternA ? $patternB : $patternA;
foreach($exploded as $e) {
if(preg_match($currentPattern, $e)) {
//toggle
$currentPattern = $currentPattern == $patternA ? $patternB : $patternA;
$result[] = trim($e);
}
}
echo "<pre>";
var_dump($result);
echo "</pre>";
Output:
array(4) {
[0]=>
string(6) "11ff11"
[1]=>
string(6) "mm22mm"
[2]=>
string(6) "77ll77"
[3]=>
string(6) "kk22kk"
}
Here's my take. Never used lookbehinds before and well, my regex skills are not that good but this does seem to return what you want.
/^.*|(?<=[a-z]{2}\n)\d{2}[a-z]{2}\d{2}|(?<=\d{2}\n)[a-z]{2}\d{2}[a-z]{2}/

How to grab number after a word or symbol in PHP?

I want to grab a text with PHP just like for an example, There is a data "The apple=10" and I want to grab only the numbers from the data which looks exactly like that. I mean, the number's place would be after 'equals'.
and my problem is that the number from the source can be 2 or 3 characters or on the other word it is inconstant.
please help me to solve them :)
$string = "Apple=10 | Orange=3 | Banana=7";
$elements = explode("|", $string);
$values = array();
foreach($elements as $element)
{
$element = trim($element);
$val_array = explode("=", $element);
$values[$val_array[0]] = $val_array[1];
}
var_dump($values);
Output:
array(3) {
["Apple"]=> string(2) "10"
["Orange"]=> string(1) "3"
["Banana"]=> string(1) "7"
}
Hope thats how you need it :)
Well, php is a bit lazy about int conversion, so 12345blablabla can be converted to 12345:
$value = intval(substr($str, strpos($str, '=') + 1));
Of course, this is not the cleanest way but it is simple. If you want something cleaner, you could use a regexp:
preg_match ('#=([0-9]+)#', $str, $matches);
$value = intval($matches[1]) ;
Try the below code:
$givenString= "The apple=10";
$required_string = substr($givenString, strpos($givenString, "=") + 1);
echo "output = ".$required_string ; // output = 10
Using strpos() function, you can Find the position of the first occurrence of a substring in a string
and substr() function, Return part of a string.

Inserting a space 2 places after a decimal in a string

In PHP, I am importing some text files containing tables of float values that are space delimited. All values contain two decimal places. A typical line would look like this:
1.45 22.87 99.12 19.55
However, some lines, if the number before the decimal is 3 digits long, the original file sometimes does not include a space. So what should be:
1.45 122.87 99.12 19.55
comes in as:
1.45122.87 99.12 19.55
What I assume I need to do is search the string for decimals, then look 2 spaces after that, and if there is not a space there I need to add one. I just cannot for the life of me figure out the most direct way to do so.
I would use regex:
$pattern = "/(-)?\d{1,}\.\d{2}/";
preg_match_all($pattern, "1.45122.87 99.12 19.55", $matches);
print_r($matches);
DEMO
This does what you want. Probably not the most efficient way to do it though.
<?php
$line = "1.45122.87 99.12 19.55";
$length = strlen($line);
$result = '';
$i=0;
while ($i<$length)
{
if ($line[$i] == '.')
{
$result .= $line[$i];
$result .= $line[$i+1];
$result .= $line[$i+2];
$result .= ' ';
$i += 3;
}
else if ($line[$i] == ' ')
{
$i++;
}
else
{
$result .= $line[$i];
$i++;
}
}
echo $result;
?>
This is a fixed-column-width file. I would parse these by substr().
http://php.net/manual/en/function.substr.php
for ($x=0; $x<strlen($line); $x+=4) {
$parts[] = trim(substr($line, $x, 4));
}
This will get you an array in $parts of all the fields. This is untested, but should work.
$line = '1.45122.87 99.12 19.55';
preg_match_all('~([0-9]{1,3}\.[0-9]{2})~', $line, $matches);
var_dump($matches[1]);
/*
Result:
array(4) {
[0]=>
string(4) "1.45"
[1]=>
string(6) "122.87"
[2]=>
string(5) "99.12"
[3]=>
string(5) "19.55"
}
*/
you could use preg_split() to create an array of the line using regex
$lineArray = preg_split("/(\d+(\.\d{1,2})?)/", $lineOfNumbers);
This would find all instances of ####.## and not worry about the spaces
I would do something like this, say the line of decimals is in a variable called $line:
$parts = explode(' ', $line);
Now you have an array of decimals values, so
$parts[0] = "1.45"
(float)$parts[0] = 1.45
$parts[1] = "122.87"
(float)$parts[1] = 122.87
// etc...

Categories