I have the following regexp that works great.
$str = "ID: {{item:id}} & First name: {{item:first_name}} & Page Title: {{page:title}}";
preg_match_all('/(?<={{)[^}]*(?=}})/', $str, $matches);
print_r($matches);
Returns:
Array
(
[0] => Array
(
[0] => item:id
[1] => item:first_name
[2] => page:title
)
)
How do I need to modify the regex to force it to match the item:id and item:first_name only (or any other string starting with "item:")? I tried adding the "item" to the regex (in several different places) but it didn't work.
You can use:
preg_match_all('/(?<={{)item:[^}]*(?=}})/', $str, $matches);
print_r($matches[0]);
Array
(
[0] => item:id
[1] => item:first_name
)
With this you can group the tokens, so you don't need to limit the expression to any single type:
(?<={{)(.+?)(?:\:)(.+?)(?=}})
Example of utilization:
$str = "ID: {{item:id}} & First name: {{item:first_name}} & Page Title: {{page:title}}";
preg_match_all('/(?<={{)(.+?)(?:\:)(.+?)(?=}})/', $str, $matches);
$tokens = array();
foreach ($matches[0] as $i => $v) {
$tokens[$matches[1][$i]][] = $matches[2][$i];
}
echo '<pre>';
print_r($tokens);
Output:
Array
(
[item] => Array
(
[0] => id
[1] => first_name
)
[page] => Array
(
[0] => title
)
)
Related
Hello :) I am a beginner in PHP.
I tried several times but did not succeed
I would like to parse a String like :
[1,[01,11,12],[20,21,22]]
to
`
arr[0][0]=>1
arr[1][0]=>01
arr[1][1]=>11
arr[1][2]=>12
arr[2][0]=>20
arr[2][1]=>21
arr[2][2]=>22
`
You can split your string on a comma that is not enclosed by [ and ] using this regex (inspired by this answer) with preg_split:
,(?![^\[]*\])
and then trim surrounding [ and ] from the resultant parts and split those strings on commas into succeeding elements of the output array. For example:
$string = '[1,[01,11,12] ,4 ,5, [20,21,22]]';
$parts = preg_split('/,(?![^\[]*\])/', $string, -1, PREG_SPLIT_DELIM_CAPTURE);
$output = array();
foreach ($parts as $part) {
$part = trim($part, '[] ');
$output[] = explode(',', $part);
}
print_r($output);
Output:
Array
(
[0] => Array
(
[0] => 1
)
[1] => Array
(
[0] => 01
[1] => 11
[2] => 12
)
[2] => Array
(
[0] => 4
)
[3] => Array
(
[0] => 5
)
[4] => Array
(
[0] => 20
[1] => 21
[2] => 22
)
)
Demo on 3v4l.org
If you're 100% certain of the source and safety of the string, you can also just use eval:
eval("\$output = $string;");
The result will be the same.
I have music chord and lyrics like this :
<p>[Fm] [Gm] [Dm]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
<p>[A] [Asus4] [Bb]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
the "p" tags generated by wordpress editor for each line.
I collect the Chord with brackets by using :
preg_match_all("/\[[^\]]*\]/", $content , $matches);
And result is :
[0] => [Fm]
[1] => [Gm]
[2] => [Dm]
[3] => [A]
[4] => [Asus4]
[5] => [Bb]
but I need to collect the chords from right to left and my array should be like this one :
[0] => [Dm]
[1] => [Gm]
[2] => [Fm]
[3] => [Bb]
[4] => [Asus4]
[5] => [A]
Thanks.
You could try to capture a single line first by matching to: (\[[^\]]*\])+(\<\\p\>){1}
You can then take a matched string and match it against your expression: /\[[^\]]*\]/.
If you take these secondary matches and put them in a 2d array you'll now have a object with this structure:
[0] => [Fm, Gm, Dm]
[1] => [A, Asus4, Bd]
Some (pseudo)code:
$outputarr = [];
preg_match_all("/(\[[^\]]*\])+(\<\\p\>){1}/", $content , $matches);
foreach ($matches as &$value) {
$internalarray = [];
preg_match_all("//\[[^\]]*\]/", $value , $matches2);
foreach ($matches2 as &$value2) {
array_push($internalarray, $value2);
}
array_push($outputarr, $internalarray);
}
You can now call the array_reverse function on elements [0] and [1] to get:
[0] => [Dm, Gm, Fm]
[1] => [Bd, Asus4, A]
And you can finally go from that to a single array by doing this:
$arr = [];
foreach ($outputarr as &$value) {
$arr = array_merge($arr, $value);
}
This should give your wanted inside variable $arr:
[0] => [Dm]
[1] => [Gm]
[2] => [Fm]
[3] => [Bb]
[4] => [Asus4]
[5] => [A]
ps: I haven't taken the whitespace characters into account and ((\[[^\]]*\])|\s)+(\<\\p\>){1} could be the better one. Furthermore this code could be improved by using array_merge and as such bypassing manual array_push
$content = "<p>[Fm] [Gm] [Dm]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>
<p>[A] [Asus4] [Bb]</p>
<p>لورم ایپسوم ، لورم ایپسوم</p>";
preg_match_all("/<p>.*<\/p>/", $content , $nodes);
$final = [];
foreach ($nodes[0] as $node ) {
if(!empty($node)) {
preg_match_all("/\[[^\]]*\]/", $node , $matches);
if(!empty($matches[0])) {
$final = array_merge($final, array_reverse($matches[0]));
}
}
}
print_r($final);
I want to extract values from a string to call an array for basic template functionality:
$string = '... #these.are.words-I_want.to.extract# ...';
$output = preg_replace_callback('~\#([\w-]+)(\.([\w-]+))*\#~', function($matches) {
print_r($matches);
// Replace matches with array value: $these['are']['words-I_want']['to']['extract']
}, $string);
This gives me:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => .extract
[3] => extract
)
But I'd like:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => are
[3] => words-I_want
[4] => to
[5] => extract
)
Which changes do I need to make to my regex?
It seems that the words are simply dot separated, so match sequences of what you don't want:
preg_replace_callback('/[^#.]+/', function($match) {
// ...
}, $str);
Should give the expected results.
However, if the # characters are the boundary of where the matching should take place, you would need a separate match and then use a simple explode() inside:
preg_replace_callback('/#(.*?)#/', function($match) {
$parts = explode('.', $match[1]);
// ...
}, $str);
You can use array_merge() function to merge the two resulting arrays:
$string = '... #these.are.words-I_want.to.extract# ...';
$result = array();
if (preg_match('~#([^#]+)#~', $string, $m)) {
$result[] = $m[0];
$result = array_merge($result, explode('.', $m[1]));
}
print_r($result);
Output:
Array
(
[0] => #these.are.words-I_want.to.extract#
[1] => these
[2] => are
[3] => words-I_want
[4] => to
[5] => extract
)
this is my string:
zzzzzzz-------eh="koko"------eh="zizi"--------eh="mimi"--------xxxxxx
i need a reg-expression to extract koko, zizi and mimi
but eh='zizi' is optional:
so if it doesn't exist like in :
zzzzzzz----------eh="koko"-----------------eh="mimi"----------xxxxxx
i should get only koko and mimi.
the '---' are some text.
i tried
preg_match_all('#zzzzzzz(.*eh="([^"]*)"){2,3}.*xxxxxx#Uix', $strg , $out, PREG_SET_ORDER);
but it doesn't work.
note: the whole thing is like :
zzzzzzz--...--xxxxxxzzzzzzz--...--xxxxxxzzzzzzz--...--xxxxxxzz...
i need them grouped
like :
array():{
[0]:array():{
[0]:"zizi",
[1]:"mimi",
[2]:"koko",
},
[1]:array():{
[0]:"zizi",
[1]:"koko",
},
[2]:array():{
[0]:"zizi",
[1]:"fofo",
[2]:"bingo",
},
}
Why not just:
preg_match_all('/(?<=eh=")([^"]+)(?=")/', $strg, $out, PREG_SET_ORDER);
Regex101 Demo
This can be solved by capturing all strings preceded by =" until a " is found:
$s='zzzzzzz-------eh="koko"------eh="zizi"--------eh="mimi"--------xxxxxx';
if (preg_match_all('~(?<==")[^"]*~', $s, $arr))
print_r($arr);
OUTPUT:
Array
(
[0] => Array
(
[0] => koko
[1] => zizi
[2] => mimi
)
)
How about:
$str = 'zzzzzzz-------eh="koko"------eh="zizi"--------eh="mimi"--------xxxxxxzzzzzzz----------eh="koko"-----------------eh="mimi"----------xxxxxx';
$arr = preg_split('/(?<=x)(?=z)/', $str);
foreach($arr as $s) {
preg_match_all('/"([^"]+)"/', $s, $out);
$arr_out[] = $out[1];
}
print_r($arr_out);
output:
Array
(
[0] => Array
(
[0] => koko
[1] => zizi
[2] => mimi
)
[1] => Array
(
[0] => koko
[1] => mimi
)
)
String to parse:
$str = "
public $xxxx123;
private $_priv ;
$xxx = 'test';
private $arr_123 = array();
"; // | |
// ^^^^^^^---- get the variable name
What I got so far:
$str = preg_match_all('/\$\S+(;|[[:space:]])/', $str, $matches);
foreach ($matches[0] as $match) {
$match = str_replace('$', '', $match);
$match = str_replace(';', '', $match);
}
It works but I want to know if I can improve the preg, e.g. get rid of the two str_replace and maybe include \t in (;|[[:space:]])
Using a positive lookbehind, you can get only that what you need, to be sure you'll only match valid variable names, I've used this:
preg_match_all('/(?<=\$)[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/',$str,$matches);
var_dump($matches);
which correctly shows:
array (
0 =>
array (
0 => 'xxxx123',
1 => '_priv',
2 => 'xxx',
3 => 'arr_123'
)
)
Which is all you need, no memory waisted on an array containing all variables with their leading and/or trailing chars.
The expression:
(?<=\$) is a positive lookbehind
[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*: is the regex PHP's site suggests themselves on their document pages
simply use backreferences
preg_match_all('/\$(\S+?)[;\s=]/', $str, $matches);
foreach ($matches[1] as $match) {
// $match is now only the name of the variable without $ and ;
}
I changed the regex a little bit, take a look:
$str = '
public $xxxx123;
private $_priv ;
$xxx = "test";
private $arr_123 = array();
';
$matches = array();
//$str = preg_match_all('/\$(\S+)[; ]/', $str, $matches);
$str = preg_match_all('/\$(\S+?)(?:[=;]|\s+)/', $str, $matches); //credits for mr. #booobs for this regex
print_r($matches);
The output:
Array
(
[0] => Array
(
[0] => $xxxx123;
[1] => $_priv
[2] => $xxx
[3] => $arr_123
)
[1] => Array
(
[0] => xxxx123
[1] => _priv
[2] => xxx
[3] => arr_123
)
)
Now you can use the $matches[1] in the foreach loop.
::Update::
After using regex "/\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)/" the output looks correct.
String:
$str = '
public $xxxx123; $input1;$input3
private $_priv ;
$xxx = "test";
private $arr_123 = array();
';
And the output:
Array
(
[0] => Array
(
[0] => $xxxx123
[1] => $input1
[2] => $input3
[3] => $_priv
[4] => $xxx
[5] => $arr_123
)
[1] => Array
(
[0] => xxxx123
[1] => input1
[2] => input3
[3] => _priv
[4] => xxx
[5] => arr_123
)
)