Checking if an array key contains a value? [duplicate] - php

This question already has an answer here:
Splitting an array key and seeing if it contains a value
(1 answer)
Closed 6 years ago.
I have an array, I want to check if the keys have '-main' after them.
foreach ($data as $key => $value) {
if (substr($key, -5) == '-main'){
....
}
}
If they do have '-main' I then want to get the text prior to '-main'. I do:
$myVar = substr($key, 0, -5);
Is there a more efficient way of splitting the key so I don't have to do two sub strings?
Perhaps I do not want to use '-main' any more and want to use a different length search item, perhaps as a variable. I would then have to do a character count rather than specifying -5. Is there a way to incorporate a variable without having to do character counts?

You can use a regex search (http://php.net/manual/de/function.preg-match.php) to do the task.
if (preg_match('/^(.*)(-main)$/', $key, $hit)) {
$myVar = $hit[1];
// explanation:
// $hit[0] will contain the whole result string
// $hit[1] will contain the part before "-main"
// $hit[2] will be "-main"
}
The regular expression is the following:
/ ... / -> requiered by preg_match
^ -> beginning of string (we want to start at the first position
(-main) -> the search text we're using
$ -> end of the string (we don't want a -main in the middle of the string to match)
(.*) anything else in the string between the start and "-main" - the paranthesis means, that this will be returned as one part of $hits
If you switch the text you want to search for, keep in mind that certain characters have a special meaning in a regex. So you might need to escape them.

Related

Delete multiple file for/while

I have a php pull down that I select an item and delete
all files associated with it.
It works well if there was only 5 or 6. After I put the
first 4 to test and get it working I realized it could
take a very long time to enter in a couple hundred and
would blot the script.
Not knowing enough about for and while loops is there
anyone that might have a way to help?
There will never be more than one set deleted at a time.
Thanks in advance.
<?php
$workitem = $_POST["workitem"];
$workdirPAth = "/var/work.files/";
if($workitem == 'item1.php')
{
unlink("$workdirPath/page1.php");
unlink("$workdirPath/temp1.php");
unlink("$workdirPath/all1.php");
}
if($workitem == 'item2.php')
{
unlink("$workdirPath/page2.php");
unlink("$workdirPath/temp2.php");
unlink("$workdirPath/all2.php");
}
if($workitem == 'item3.php')
{
unlink("$workdirPath/page3.php");
unlink("$workdirPath/temp3.php");
unlink("$workdirPath/all3.php");
}
if($workitem == 'item4.php')
{
unlink("$workdirPath/page4.php");
unlink("$workdirPath/temp4.php");
unlink("$workdirPath/all3.php");
?>
Some simple pattern matching and substitution is all you need here.
First, the code:
1. if (preg_match('/^item(\d+)\.php$/', $workitem, $matches)) {
2. $number = $matches[1];
3. foreach(array('page','temp','all') as $base) {
4. unlink("$workdirPath/$base$number.php");
5. }
6. } else {
7. # unrecognized work item value; complain to user or whatever
8. }
The preg_match function takes a pattern, a string, and an array. If the string matches the pattern, the parts that match are stored in the array. The particular type of pattern is a *p*erl5-compatible *reg*ular expression, which is where the preg_ part of the name comes from.
Regular expressions are scary-looking to the uninitiated, but they're a handy way to scan a string and get some values out of it. Most characters just represent themselves; the string "foo" matches the regular expression /foo/. But some characters have special meanings that let you make more general patterns to match a whole set of strings where you don't have to know ahead of time exactly what's in them.
The /s just mark the beginning and end of the actual regular expression; they're there because you can stick additional modifier flags inside the string along with the expression itself.
The ^and $ arepresent the beginning and end of the string. "/foo/" matches "foo", but also "foobar", "bunnyfoofoo", and so on - any string that contains "foo" will match. But /^foo$/ matches only "foo" exactly.
\d means "any digit". + means "one or more of that last thing". So \d+ means "one or more digits".
The period (.) is special; it matches any character at all. Since we want a literal period, we have to escape it with a backslash; \. just matches a period.
So our regular expression is '/^item\d+\.php$/', which will match any itemnumber.php filename. But that's not quite enough. The preg_match function is basically a binary test: does the string match the pattern or not, yes or no? In this case, it's not enough to just say "yup, the string is valid"; we need to know which items specifically the user specified. That's what capture groups are for. We use parentheses to say "remember what matched this part", and provide an array name that gets filled with those remembrances.
The part of the string that matches the whole regular expression (which may not be the whole string, if the regular expression isn't anchored with ^...$ like this one is) is always put in element 0 of the array. If you use parentheses in the regular expression, then the part of the string that matches the part of the regular expression inside the first pair of parentheses is stored in element 1 of the array; if there's a second set of parentheses, the matching part of the string goes in element 2 of the array, and so on.
So we put parentheses around our number ((\d+)) and then the actual number will be remembered in element 1 of our $matches array.
Great, we have a number. Now we just need to use it to build up the filenames we want to delete.
In each case, we want to delete three files: page$n.php, temp$n.php, and all$n.php, where $n is the number we extracted above. We could just put three unlink calls, but since they're all so similar, we can use a loop instead.
Take the different prefixes that are the same no matter the number, and make an array out of them. Then loop over that array. In the body of the loop, the variable $base will contain whichever element of the array it's currently on. Stick that between the $workdirPath prefix and the $number we got from the match, append .php, and that's your file. unlink it and go back to the top of the loop to grab the next one.

Find nth character except if its enclosed in brackets php

I use the following function to find the nth character in a string which works well. However there is one exception, lets say its a comma for this purpose, what i need to alter about this is that if the coma is within ( and ) then it shouldnt count that
function strposnth($haystack, $needle, $nth=1, $insenstive=0)
{
//if its case insenstive, convert strings into lower case
if ($insenstive) {
$haystack=strtolower($haystack);
$needle=strtolower($needle);
}
//count number of occurances
$count=substr_count($haystack,$needle);
//first check if the needle exists in the haystack, return false if it does not
//also check if asked nth is within the count, return false if it doesnt
if ($count<1 || $nth > $count) return false;
//run a loop to nth number of occurrence
//start $pos from -1, cause we are adding 1 into it while searching
//so the very first iteration will be 0
for($i=0,$pos=0,$len=0;$i<$nth;$i++)
{
//get the position of needle in haystack
//provide starting point 0 for first time ($pos=0, $len=0)
//provide starting point as position + length of needle for next time
$pos=strpos($haystack,$needle,$pos+$len);
//check the length of needle to specify in strpos
//do this only first time
if ($i==0) $len=strlen($needle);
}
//return the number
return $pos;
}
So ive got the regex working that only captures the comma when outside of () which is:
'/,(?=[^)]*(?:[(]|$))/'
and you can see a live example working here:
http://regex101.com/r/xE4jP8
but im not sure how to make it work within the strpos loop, i know what i need to do, tell it the needle has this regex exception but i am not sure how to make it work. Maybe i should ditch the function and use another method?
Just to mention my end result i want is to split the string after every 6 commas before the next string starts, example:
rttr,ertrret,ertret(yes,no),eteert,ert ert,rtrter,0 rttr,ert(yes,no)rret,ert ret,eteert,ertert,rtrter,1 rttr,ertrret,ert ret,eteert,ertert,rtrter,0 rttr,ertrret,ert ret,eteert,ertert,rtrter,2 rttr,ert(white,black)rret,ert ret,eteert,ertert,rtrter,0 rttr,ertrret,ert ret,eteert,ertert,rtrter,0 rttr,ertrret,ert ret,et(blue,green)eert,ertert,rtrter,1
Note that there is always a 1 digit number (1-3) and a space after the 6th comma before the next part of the string begins but i cant really rely on that as its possible earlier in the string this pattern could happen so i can always rely on the fact ill need to split the string after the first digit and space after the 6th comma. So i want to split the string directly after this.
For example the above string would be split like this:
rttr,ertrret,ertret(yes,no),eteert,ert ert,rtrter,0
rttr,ert(yes,no)rret,ert ret,eteert,ertert,rtrter,1
rttr,ertrret,ert ret,eteert,ertert,rtrter,0
rttr,ertrret,ert ret,eteert,ertert,rtrter,2
rttr,ert(white,black)rret,ert ret,eteert,ertert,rtrter,0
rttr,ertrret,ert ret,eteert,ertert,rtrter,0
rttr,ertrret,ert ret,et(blue,green)eert,ertert,rtrter,1
I can do that myself pretty easily if i know how to get the position of the character then i can use substr to split it but an easier way might be preg_split but im not sure how that would work until i figure this part out
I hope i wasnt too confusing in explaining, i bet i was :)
For these kind of nesting problems regex usually is not the right tool. However, when the problem is actually not that complicated, as yours seems to be, regex will do just fine.
Try this:
(?:^|,)((?:[^,(]*(?:\([^)]*\))?)*)
^ start the search with a comma or the start of the string
^ start non capture group
^ search until comma or open parenthesis
^ if parenthesis found then capture until
^ end of parenthesis
^ end of capture group repeat if necessary
See it in action: http://regex101.com/r/eS0cX4
As you can see this will capture everything between the comma's outside of the parenthesis. If you get all these matches into an array using preg_match_all you can split it any which way you like.

Read corresponding value in PHP and add to running sum

I would like to have each word in a string cross-referenced in a file.
So, if I was given the string: Jumping jacks wake me up in the morning.
I use some regex to strip out the period. Also, the entire string is made lowercase.
I then go on to have the words separated into an array by using PHP's nifty explode() function.
Now, what I'm left with, is an array with the words used in the string.
From there I need to look up each value in the array and get a value for it and add it to a running sum. for() loop it is. Okay, this is where I get stuck...
The list ($wordlist) is structured like so:
wake#4 waking#3 0.125
morning#2 -0.125
There are \ts in between the word and the number. There can be more than one word per value.
What I need the PHP to do now is look up the number to each word in the array then pull that corresponding number back to add it to a running sum. What's the best way for me to go about this?
The answer should be easy enough, just finding the location of the string in the wordlist and then finding the tab and from there reading the int... I just need some guidance.
Thanks in advance.
EDIT: to clarify -- I don't want the sum of the values of the wordlist, rather, I'd like to look up my individual values as they correspond to the words in the sentence and THEN look them up in the list and add just those values; not all of them.
Edited answer based on your comment and question edit. The running sum is stored in an array called $sum where the key value of the "word" will store the value of its running sum. e.g $sum['wake'] will store the running sum for the word wake and so on.
$sum = array();
foreach($wordlist as $word) //Loop through each word in wordlist
{
// Getting the value for the word by matching pattern.
//The number value for each word is stored in an array $word_values, where the key is the word and value is the value for that word.
// The word is got by matching upto '#'. The first parenthesis matches the word - (\w+)
//The word is followed by #, single digit(\d), multiple spaces(\s+), then the number value(\S+ matches the rest of the non-space characters)
//The second parenthesis matches the number value for the word
preg_match('/(\w+)#\d\s+(\S+)/', $word, $match);
$word_ref = $match[1];
$word_ref_number = $match[2];
$word_values["$word_ref"] = $word_ref_number;
}
//Assuming $sentence_array to store the array of words used in your string example {"Jumping", "jacks", "wake", "me", "up", "in", "the", "morning"}
foreach ($sentence_array as $word)
{
if (!array_key_exists("$word", $sum)) $sum["$word"] = 0;
$sum["$word"] += $word_values["$word"];
}
Am assuming you would take care of case sensitivities, since you mentioned that you make the entire string lowercase, so am not including that here.
$sentence = 'Jumping jacks wake me up in the morning';
$words=array();
foreach( explode(' ',$sentence) as $w ){
if( !array_key_exists($w,$words) ){
$words[$w]++;
} else {
$words[$w]=1;
}
}
explodeby space, check if that word is in the words array as key; if so increment it's count(val); if not, set it's val as 1. Loop this for each of your sentences without redeclaring the $words=array()

Filter array of numeric PIN code strings which may be in the format "######" or "### ###"

I have a PHP array of strings. The strings are supposed to represent PIN codes which are of 6 digits like:
560095
Having a space after the first 3 digits is also considered valid e.g. 560 095.
Not all array elements are valid. I want to filter out all invalid PIN codes.
Yes you can make use of regex for this.
PHP has a function called preg_grep to which you pass your regular expression and it returns a new array with entries from the input array that match the pattern.
$new_array = preg_grep('/^\d{3} ?\d{3}$/',$array);
Explanation of the regex:
^ - Start anchor
\d{3} - 3 digits. Same as [0-9][0-9][0-9]
? - optional space (there is a space before ?)
If you want to allow any number of any whitespace between the groups
you can use \s* instead
\d{3} - 3 digits
$ - End anchor
Yes, you can use a regular expression to make sure there are 6 digits with or without a space.
A neat tool for playing with regular expressions is RegExr... here's what RegEx I came up with:
^[0-9]{3}\s?[0-9]{3}$
It matches the beginning of the string ^, then any three numbers [0-9]{3} followed by an optional space \s? followed by another three numbers [0-9]{3}, followed by the end of the string $.
Passing the array into the PHP function preg_grep along with the Regex will return a new array with only matching indeces.
If you just want to iterate over the valid responses (loop over them), you could always use a RegexIterator:
$regex = '/^\d{3}\s?\d{3}$/';
$it = new RegexIterator(new ArrayIterator($array), $regex);
foreach ($it as $valid) {
//Only matching items will be looped over, non-matching will be skipped
}
It has the benefit of not copying the entire array (it computes the next one when you want it). So it's much more memory efficient than doing something with preg_grep for large arrays. But it also will be slower if you iterate multiple times (but for a single iteration it should be faster due to the memory usage).
If you want to get an array of the valid PIN codes, use codaddict's answer.
You could also, at the same time as filtering only valid PINs, remove the optional space character so that all PINs become 6 digits by using preg_filter:
$new_array = preg_filter('/^(\d{3}) ?(\d{3})$/D', '$1$2', $array);
The best answer might depend on your situation, but if you wanted to do a simple and low cost check first...
$item = str_replace( " ", "", $var );
if ( strlen( $item ) !== 6 ){
echo 'fail early';
}
Following that, you could equally go on and do some type checking - as long as valid numbers did not start with a 0 in which case is might be more difficult.
If you don't fail early, then go on with the regex solutions already posted.

How to split a string and find the occurence of one string in another?

I need to figure out how to do some C# code in php, and im not sure exactly how.
so first off i need the Split function, im going to have a string like
"identifier 82asdjka271akshjd18ajjd"
and i need to split the identifier word from the rest. so in C#, i used string.Split(new char{' '}); or something like that (working off the top of my head) and got two strings, the first word, and then the second part.. i understand that the php split function has been deprecated as of PHP 5.3.0.. so thats not an option, what are the alternatives?
and im also looking for a IndexOf function, so if i had the above code again as an example, i would need the location of 271 in the string, so i can generate a substring.
you can use explode for splitting and strpos for finding the index of one string inside another.
$a = "identifier 82asdjka271akshjd18ajjd";
$arr = explode(' ',$a); // split on space..to get an array of size 2.
$pos = strpos($arr[1],'271'); // search for '271' in the 2nd ele of array.
echo $pos; // prints 8

Categories