Related
I have an array with rule field that has a string like this:
FREQ=MONTHLY;BYDAY=3FR
FREQ=MONTHLY;BYDAY=3SA
FREQ=WEEKLY;UNTIL=20170728T080000Z;BYDAY=MO,TU,WE,TH,FR
FREQ=MONTHLY;UNTIL=20170527T100000Z;BYDAY=4SA
FREQ=WEEKLY;BYDAY=SA
FREQ=WEEKLY;INTERVAL=2;BYDAY=TH
FREQ=WEEKLY;BYDAY=TH
FREQ=WEEKLY;UNTIL=20170610T085959Z;BYDAY=SA
FREQ=MONTHLY;BYDAY=2TH
Each line is a different array, I am giving a few clues to get an idea of what I need.
What I need is to write a regex that would take off all unnecessary values.
So, I don't need FREQ= ; BYDAY= etc. I basically need the values after = but each one I want to store in a different variable.
Taking third one as an example it would be:
$frequency = WEEKLY
$until = 20170728T080000Z
$day = MO, TU, WE, TH, FR
It doesn't have to be necessarily one regex, there can be one regex for each value. So I have one for FREQ:
preg_match("/[^FREQ=][A-Z]+/", $input_line, $output_array);
But I can't do it for the rest unfortunately, how can I solve this?
The only way to go would be PHP array destructuring:
$str = "FREQ=WEEKLY;UNTIL=20170728T080000Z;BYDAY=MO,TU,WE,TH,FR";
preg_match_all('~(\w+)=([^;]+)~', $str, $matches);
[$freq, $until, $byday] = $matches[2]; // As of PHP 7.1 (otherwise use list() function)
echo $freq, " ", $until, " ", $byday;
// WEEKLY 20170728T080000Z MO,TU,WE,TH,FR
Live demo
Be more general
Using extract function:
preg_match_all('~(\w+)=([^;]+)~', $str, $m);
$m[1] = array_map('strtolower', $m[1]);
$vars = array_combine($m[1], $m[2]);
extract($vars);
echo $freq, " ", $until, " ", $byday;
Live demo
Notice: For this problem, I recommend the generell approach #revo posted, it's concise and safe and easy on the eyes -- but keep in mind, that regular expressions come with a performance penalty compared to fixed string functions, so if you can use strpos/substr/explode/..., try to use them, don't 'knee-jerk' to a preg_-based solution.
Since the seperators are fixed and don't seem to occur in the values your are interested in, and you furthermore rely on knowledge of the keys (FREQ:, etc) you don't need regular-expressions (as much as I like to use them anywhere I can, and you can use them here); why not simply explode and split in this case?
$lines = explode("\n", $text);
foreach($lines as $line) {
$parts = explode(';', $line);
$frequency = $until = $day = $interval = null;
foreach($parts as $part) {
list($key, $value) = explode('=', $part);
switch($key) {
case 'FREQ':
$frequency = $value;
break;
case 'INTERVAL':
$interval = $value;
break;
// and so on
}
}
doSomethingWithTheValues();
}
This may be more readable and efficient if your use-case is as simple as stated.
You need to use the Pattern
;?[A-Z]+=
together with preg_split();
preg_split('/;?[A-Z]+=/', $str);
Explanation
; match Semikolon
? no or one of the last Character
[A-Z]+ match one or more uppercase Letters
= match one =
If you want to have each Line into a seperate Array, you should do it this Way:
# split each Line into an Array-Element
$lines = preg_split('/[\n\r]+/', $str);
# initiate Array for Results
$results = array();
# start Looping trough Lines
foreach($lines as $line){
# split each Line by the Regex mentioned above and
# put the resulting Array into the Results-Array
$results[] = preg_split('/;?[A-Z]+=/', $line);
}
So I have a list of values like that goes like this:
values: n,b,f,d,e,b,f,ff`
I want to use preg_replace() in order to remove the repeated characters from the list of values (it will be inserted to a MySQL table). b and f are repeated. ff should not count as f because it's a different value. I know that \b \b will be used for that. I am not sure on how to take out the repeated b and f values as well as the , that precedes each value.
If the list is in a string looking like the example above, a regex is overkill. This does it just as well;
$value = implode(',', array_unique(explode(',', $value)));
I agree with other commenters that preg_replace is not the way to go; but, since you ask, you can write:
$str = preg_replace('/\b(\w+),(?=.*\b\1\b)/', '', $str);
That will remove all but the last instance of a given list-element.
No need for regex for this:
join(",", array_unique(split(",", $values)))
If this list you're dealing with is a simple string, a possible solution would be like this:
function removeDuplicates($str) {
$arr = explode(',', $str);
$arr = array_unique($arr);
return implode(',', $arr);
}
$values = removeDuplicates('n,b,f,d,e,b,f,ff'); // n,b,f,d,e,ff
$str = "values: n,b,f,d,e,b,f,ff";
$arr = array();
preg_match("/(values: )([a-z,]+)/i", $str, $match);
$values = explode(",", $match[2]);
foreach($values AS $value){
if(!$arr[$value]) $arr[$value] = true;
}
$return = $match[1];
foreach($arr AS $a){
$return .= ($i++ >= 1 ? "," : "").$a;
}
I have this variable : $lang['_city']
I need to divide it in two portions.
One portion will be $key == lang and second portion will be $ind == _city
I think I can do it with some sort of regexp but I am not good with it.
Can you help me?
P.S. The above example is only a sample. I need an abstract function that will do it with any passed variable.. So for example, get whatever is before [ and cut the $, and then get whatever is inside [' '].. I know the logic but not the way to do it :)
Also the new values will have to be assigned each one to a variable as explained before.
Thanks
$string = "\$foo['bar']";
preg_match('~^\$(?\'key\'[^\[]+)\[\'(?\'ind\'[^\']+)~', $string, $matches);
var_dump($matches['key'], $matches['ind']);
http://ideone.com/XaopX
Regexp for preg_match would be: ~^\$([^\[]+)\[\s*("[^"]+"|'[^']+')\s*\]$~ (you'll need to escape ' or " when moving to string).
Than:
$key = $match[1];
$ind = substr( $match[2], 1, -1);
You do not even need regexp here :)
$str = "\$lang['city']";
$tmp = explode('[', $str);
$key = ltrim($tmp[0], '$');
$ind = trim($tmp[1], ']\'"');
echo $key . '=>' . $ind;
Starting from PHP 5.4 you could do it even shorter
$str = "\$lang['city']";
$key = ltrim(explode('[', $str)[0], '$');
$ind = trim(explode('[', $str)[1], ']\'"');
echo $key . '=>' . $ind;
I have a value from my database: 250.00$ 100x100cm or 1960.00$ 500x500cm
How do I make it so that it cuts away the price and only shows the measurements?
So it should only print out: 100x100cm or 500x500cm
I hope that's easy enough to understand.
Use explode( ' ', $value); and pull the value from [1] in the resulting array.
echo array_pop( explode( ' ', '250.00$ 100x100cm' ) ); // 100x100cm
My example is doing something similar - using array_pop() I'm retrieving the value of the last index in the resulting array from explode() and returning it to be echo'd.
Assuming that's a single space separating the pieces and there are no other spaces:
list($price, $measurement) = explode(" ", $value, 2);
Note that the price is optional if you don't want it:
list(, $measurement) = explode(" ", $value, 2);
And the "2" is just for a bit of safety in case the measurement has a space.
if thats always the format I would try the explode php function. Something like;
$array = explode("$ ", "250.00$ 100x100cm");
You can then just echo out which part of the array you need...
Try this:
$value = "250.00$ 100x100cm";
$value_parts = explode(" ", $value);
echo $value_parts[1];
Is it always going to be a three digit number?
echo substr($string, -7);
Otherwise you might want some regex
preg_match("!\d+x\d+cm!", $value);
If you always have a space between the price and dimensions, you can use
$newVar = explode(" ", $yourVar); to split the value by a space and then just use echo $newVar[1] to show your dimensons.
foreach ($values as $value) {
list($price, $size) = explode(' ', $value);
echo $size;
}
I've got a list of names separated by commas (they may contain other characters), or be empty, but generally looking like this:
NameA,NameB,NameC
I need to create a function to delete a name if its present in the list and which restores the comma separated structure.
eg: if NameA is to be deleted, I should end up with:
NameB,NameC
NOT
,NameB,NameC
Similarly for the rest.
This is what I came up with, is there a better solution?
$pieces = explode(",", $list);
$key=array_search($deleteuser, $pieces);
if(FALSE !== $key)
{
unset($pieces[$key]);
}
$list = implode(",", $pieces);
That should work pretty well. You may also be interested in PHP's fgetcsv function.
Doc: http://php.net/manual/en/function.fgetcsv.php
You could use the array_splice function to delete from the array. With offset = array_search($deleteuser, $pieces) and length = 1.
If your application data is so large that you are experiencing a crippling amount of lag, then you may have bigger issues that this snippet of code. It might be time to rethink your data storage and processing from the ground up.
In the meantime, here are some benchmarks when imploding a 5001 element array with commas, then using different techniques to remove value 4999. (speeds actually include the generation of the comma-separate string, but all benchmarks are identical in this regard)
explode() + array_search() + unset() + implode() (demo)System time: ~.009 - .011s
$pieces = explode(",", $list);
if (($key = array_search($deleteuser, $pieces)) !== false) {
unset($pieces[$key]);
}
echo implode(",", $pieces);
explode() + array_search() + array_splice() + implode() (demo)System time: ~.010 - .012s
$pieces = explode(",", $list);
if (($key = array_search($deleteuser, $pieces)) !== false) {
array_splice($pieces, $key, 1);
}
echo implode(",", $pieces);
explode() + foreach() + if() + unset() + break + implode() (demo)System time: ~.011 - .012s
$pieces = explode(",", $list);
foreach ($pieces as $key => $value) {
if ($value == $deleteuser) {
unset($pieces[$key]);
break;
}
}
echo implode(",", $pieces);
(note: if you remove the break, the loop can remove multiple occurrences of the needle)
explode() + array_diff() + implode() (demo)System time: ~.010 - .011s
$pieces = explode(",", $list);
$pieces = array_diff($pieces, [$deleteuser]);
echo implode(",", $pieces);
// or just: echo implode(',', array_diff(explode(",", $list), [$deleteuser]);
explode() + array_filter() + implode() (demo)System time: ~.010 - .013s
$pieces = explode(",", $list);
$pieces = array_filter($pieces, function($v) use ($deleteuser) {
return $v != $deleteuser;
});
echo implode(",", $pieces);
preg_quote() + preg_replace() (demo) (regex demo)System time: ~.007 - .010s
$needle = preg_quote($deleteuser, '/');
echo preg_replace('/,' . $needle . '(?=,|$)|^' . $needle . ',/', '', $list);
// if replacing only one value, declare preg_replace's 4th parameter as 1
Note that if you are using a delimiting character that has a special meaning to the regex engine (like +), then you will need to add a slash before it \+ to escape it and make it literal. This would make the pattern: '/\+' . $needle . '(?=\+|$)|^' . $needle . '\+/'
So while the regex-based snippet proved to be slightly (unnoticeably) faster for my arbitrarily conjured string length, you will need to make your own benchmarks to be sure which is the best performer for your application.
That said, and don't get me wrong I love regex, but the regular expression is going to be the easiest for developers to "get wrong" when it is time to modify the pattern AND I am confident that most devs will agree it has the worst overall comprehensibility.
You could also try a regular expression like this (maybe it can be optimized):
$search = 'NameB';
$list = 'NameA,NameB,NameC';
$list = preg_match('/(^' . $search . ',)|(,' . $search. ')/', '', $list);