What am I doing wrong in this php regular expression? - php

I have a problem. I don't know what could be the cause. What I want to do is to know if an element of an array has a certain word, and I use this regular expression /.*example.*/, and this is the code:
$array = ['example1', '2example', 'no'];
$matches = [];
$var = "example";
foreach($array as $element)
{
preg_match("/.*$var.*/", $element, $matches);
}
But when I run the above code and see the value of $matches it is an empty array. What am I doing wrong?

That's because you are looping through your $array and probably print the result after the loop.
So $matches just includes the matching elements of the last item in your $array.
But because 'no' is the last element, and it doesn't fulfill the regex requirements, $matches is empty.
To have a better understanding of what is happening, try to use print_r($matches) within your loop, after you called preg_matches().
And after that, try to call it after your loop and see the difference.

You need two variables. One is the result of the current match, and another is a list of all the matches. After each call you can push the result of the current match to the list.
$array = ['example1', '2example', 'no'];
$matches = [];
$var = "example";
foreach($array as $element)
{
if (preg_match("/.*$var.*/", $element, $match)) {
$matches[] = $match[0];
}
}
print_r($matches);

Related

Replacing values in string

I have a string which contains certain number of #{number} structures. For example:
#328_#918_#1358
SKU:#666:#456
TEST--#888/#982
For each #{number} structure, I have to replace it with a known string.
For the first example:
#328_#918_#1358
I have the following strings:
328="foo"
918="bar"
1358"arg"
And the result should be:
foo_bar_arg
How do I achieve such effect? My current code looks like that:
$matches = array();
$replacements = array();
// starting string
$string = "#328_#918:#1358";
// getting all the numbers from the string
preg_match_all("/\#[0-9]+/", $string, $matches);
// getting rid of #
foreach ($matches[0] as $key => &$feature) {
$feature = preg_replace("/#/", "", $feature);
} // end foreach
// obtaining the replacement values
foreach ($matches[0] as $key => $value) {
$replacement[$value] = "fizz"; // here the value required for replacement is obtained
} // end foreach
But I have no idea how to actually perform a replacement in $string variable using values from $replacement table. Any help is much appreciated!
You can use a preg_replace_callback solution:
$string = '#328_#918:#1358
SKU:#666:#456
TEST--#888/#982';
$replacements = [328=>"foo", 918=>"bar", 1358=>"arg"];
echo preg_replace_callback("/#([0-9]+)/", function ($m) use ($replacements) {
return isset($replacements[$m[1]]) ? $replacements[$m[1]] : $m[0];
}
,$string);
See the PHP demo.
The #([0-9]+) regex will match all non-overlapping occurrences of # and one or more digits right after capturing them into Group 1. If there is an item in the replacements associative array with the numeric key, the whole match is replaced with the corresponding value. Else, the match is returned so that no replacement could occur and the match does not get removed.

preg_match Array String Replacement

I have an array $track['context'] This outputs the following
常州, 的, 妈咪ZA, 已揽件快件已从, 常州, 发出快件到达
Each one of these are tracking details.
I am running the array through the following code to try and run a preg_match for each item that is inside of $track['context'] then replace if a string from $badWords is present
$badWords = ['常州', '的']; // I would want these to end up being ['one', 'two']
$arrayToCheck = $track['context'];
foreach ($badWords as $badWord) {
if (preg_match("/($badWord)/", $arrayToCheck)) {
// Do I run my preg_match function here?
}
}
I would suggest a data structure for the bad words, where the word is the key and the replacement the value in an associative array.
Then you could loop over your content array, and do the replacement with a callback function:
// Sample data:
$track['context'] = array(
'qdf 常州',
'fdhlkjfq fdkq ',
'的 fdsqfsf'
);
// Make a translation table for the bad words:
$badWords = [
'常州' => 'one',
'的' => 'two'
];
// Build a regular expression that matches any of the above words:
$regexp = "/\b(" . implode('|', array_map('preg_quote', array_keys($badWords))) . ")\b/u";
// Iterate over the content
foreach ($track['context'] as &$subject) {
$subject = preg_replace_callback($regexp, function($matches) use ($badWords) {
// Replace the matched bad word with what we have mapped for it:
return $badWords[$matches[0]];
}, $subject);
}
// Output results:
print_r ($track['context']);
See it run on eval.in
First of all, make $badWords an array, like:
$badWords = array('bad_word1', 'bad_word2');
Second, I would use the strpos function, which is used to find the occurrence of one string inside other.
Lastly, remember to set $noBadWordsFound to false in your code.

regular expression word preceded by char

I want to grab a specific string only if a certain word is followed by a = sign.
Also, I want to get all the info after that = sign until a / is reached or the string ends.
Let's take into example:
somestring.bla/test=123/ohboy/item/item=capture
I want to get item=capture but not item alone.
I was thinking about using lookaheads but I'm not sure it this is the way to go. I appreciate any help as I'm trying to grasp more and more about regular expressions.
[^/=]*=[^/]*
will give you all the pairs that match your requirements.
So from your example it should return:
test=123
item=capture
Refiddle Demo
If you want to capture item=capture, it is straightforward:
/item=[^\/]*/
If you want to also extract the value,
/item=([^\/]*)/
If you only want to match the value, then you need to use a look-behind.
/(?<=item=)[^\/]*/
EDIT: too many errors due to insomnia. Also, screw PHP and its failure to disregard separators in a character group as separators.
Here is a function I wrote some time ago. I modified it a little, and added the $keys argument so that you can specify valid keys:
function getKeyValue($string, Array $keys = null) {
$keys = (empty($keys) ? '[\w\d]+' : implode('|', $keys));
$pattern = "/(?<=\/|$)(?P<key>{$keys})\s*=\s*(?P<value>.+?)(?=\/|$)/";
preg_match_all($pattern, $string, $matches, PREG_SET_ORDER);
foreach ($matches as & $match) {
foreach ($match as $key => $value) {
if (is_int($key)) {
unset($match[$key]);
}
}
}
return $matches ?: FALSE;
}
Just trow in the string and valid keys:
$string = 'somestring.bla/test=123/ohboy/item/item=capture';
$keys = array('test', 'item');
$keyValuePairs = getKeyValue($string, $keys);
var_dump($keyValuePairs);

A bit lost with preg_match regular expression

I'm a beginner in regular expression so it didn't take long for me to get totally lost :]
What I need to do:
I've got a string of values 'a:b,a2:b2,a3:b3,a4:b4' where I need to search for a specific pair of values (ie: a2:b2) by the second value of the pair given (b2) and get the first value of the pair as an output (a2).
All characters are allowed (except ',' which seperates each pair of values) and any of the second values (b,b2,b3,b4) is unique (cant be present more than once in the string)
Let me show a better example as the previous may not be clear:
This is a string: 2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,never:0
Searched pattern is: 5
I thought, the best way was to use function called preg_match with subpattern feature.
So I tried the following:
$str = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
$re = '/(?P<name>\w+):5$/';
preg_match($re, $str, $matches);
echo $matches['name'];
Wanted output was '5 minutes' but it didn't work.
I would also like to stick with Perl-Compatible reg. expressions as the code above is included in a PHP script.
Can anyone help me out? I'm getting a little bit desperate now, as Ive spent on this most of the day by now ...
Thanks to all of you guys.
$str = '2 minutes:2,51 seconds:51,5 minutes:5,10 minutes:10,15 minutes:51,never:0';
$search = 5;
preg_match("~([^,\:]+?)\:".preg_quote($search)."(?:,|$)~", $str, $m);
echo '<pre>'; print_r($m); echo '</pre>';
Output:
Array
(
[0] => 5 minutes:5
[1] => 5 minutes
)
$re = '/(?:^|,)(?P<name>[^:]*):5(?:,|$)/';
Besides the problem of your expression having to match $ after 5, which would only work if 5 were the last element, you also want to make sure that after 5 either nothing comes or another pair comes; that before the first element of the pair comes either another element or the beginning of the string, and you want to match more than \w in the first element of the pair.
A preg_match call will be shorter for certain, but I think I wouldn't bother with regular expressions, and instead just use string and array manipulations.
$pairstring = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
function match_pair($searchval, $pairstring) {
$pairs = explode(",", $str);
foreach ($pairs as $pair) {
$each = explode(":", $pair);
if ($each[1] == $searchval) {
echo $each[0];
}
}
}
// Call as:
match_pair(5, $pairstring);
Almost the same as #Michael's. It doesn't search for an element but constructs an array of the string. You say that values are unique so they are used as keys in my array:
$str = '2 minutes:2,5 minutes:5,10 minutes:10,15 minutes:15,20 minutes:20,30 minutes:30, never:0';
$a = array();
foreach(explode(',', $str) as $elem){
list($key, $val) = explode(':', $elem);
$a[$val] = $key;
}
Then accessing an element is very simple:
echo $a[5];

Searching contents of a file using keywords inside array : PHP

I have 3 keywords inside array $val[1] :e.g one, two, three
and the following code:
foreach ($bus as $val){
$val = preg_split('/-./', $val, -1, PREG_SPLIT_NO_EMPTY); // split by _
$val[1] = trim(preg_replace('/\s*\(.*/', '', $val[1])); // remove () if found
$val[1] = trim(preg_replace("/\s*\.\.\.*/", '', $val[1])); // remove ... if found
$pattern = "/.*$val[1].*/i";
$data3 = file_get_contents("foo.txt");
preg_match_all($pattern, $data3, $matches);
foreach ($matches[0] as $v){
echo $pattern."<BR>".$v."<BR>";} }
However when I do the last echo $pattern in the loop, I found out that it only prints out /.*two.*/i (meaning it's using only the second keyword to search the file) as opposed to outside of it where it is able to print all keywords
/.*one.*/i
/.*two.*/i
/.*three.*/i
Where did I go wrong in the code? (I'll only be getting 3 lines of text as the result as each keyword will only return one result)
EDIT: Think i left out an important part of the code, I've edited it to show it more accurately - so I dont think I've been overwriting $val
Examples of what $bus will print
Ayer Rajah Avenue-.Opp JVC Electron...
Ayer Rajah Avenue-.JVC Electronics ...
Portsdown Road-.Opp Portsdown Camp ...
You are over writing your $val array!
foreach($val[1] as $keyword) //when you do "as $val" that means for the 2nd time.. $val[1] does not exist!
{
$pattern = "/.*$keyword.*/i"; ...
use
PREG_SET_ORDER
as 4th argument in preg_match_all
reference
Try using different names for your array and the element in your foreach statement.
Also I'd recommend you put file_get_contents outside your loop for efficiency reasons.
Finally, your last foreach loop should uses $matches, not $matches[0] (which just calls the first match.
You want to use something like:
foreach ($val as $keyword) {
// replace usage of $val with $keyword
You are taking the second element of the array, $val[1], which is two, and then applying a foreach against two. Then you are storing it right back on top of your original variable, $val.
Edit
You are only working with $val[1]. Don't you need to apply all of your logic to all of the entries in $val?
$val = preg_split('/-./', $val, -1, PREG_SPLIT_NO_EMPTY); // split by _
for ($i = 0; $i < count($val); $i++) {
$val[$i] = trim(preg_replace('/\s*\(.*/', '', $val[$i])); // remove () if found
$val[$i] = trim(preg_replace("/\s*\.\.\.*/", '', $val[$i])); // remove ... if found
$pattern = "/.*$val[$i].*/i";
// ...
}

Categories