Return substring in array with regex - php

I have the following string, eg: 'Hello [owner], we could not contact by phone [phone], it is correct?'.
Regex would like to return in the form of array, all that is within []. Inside the bracket will only alpha characters.
Return:
$array = [
0 => '[owner]',
1 => '[phone]'
];
How should I proceed to have this return in php?

Try:
$text = 'Hello [owner], we could not contact by phone [phone], it is correct?';
preg_match_all("/\[[^\]]*\]/", $text, $matches);
$result = $matches[0];
print_r($result);
Output:
Array
(
[0] => [owner]
[1] => [phone]
)

I'm assuming that the end goal of all of this is that you want to replace the [placeholder]s with some other text, so use preg_replace_callback instead:
<?php
$str = 'Hello [owner], we could not contact by phone [phone], it is correct?';
$fields = [
'owner' => 'pedrosalpr',
'phone' => '5556667777'
];
$str = preg_replace_callback('/\[([^\]]+)\]/', function($matches) use ($fields) {
if (isset($fields[$matches[1]])) {
return $fields[$matches[1]];
}
return $matches[0];
}, $str);
echo $str;
?>
Output:
Hello pedrosalpr, we could not contact by phone 5556667777, it is correct?

Related

preg_split how to transform the delimiter as index using php

I have this cases with strings in PHP:
*nJohn*sSmith*fGeorge#*nHenry*sFord
and wish to create an array with
[name],[surname],[fathers] as indexes so it will produce
name_array[1] = (
[name] => 'John',
[surname] => 'Smith',
[fathers] => 'George'
)
name_array[2]=(
[name] => 'Henry',
[surname] => 'Ford'
)
and so on.
How to do it using preg_split in PHP??
Thanks!
I'd use preg_match_all to get the names. If your string is consistent I think you could do:
$string = '*nJohn*sSmith*fGeorge#*nHenry*sFord';
preg_match_all('/\*n(?<givenname>.*?)\*s(?<surname>.*?)(?:\*f(?<middlename>.*?))?(?:#|$)/', $string, $matches);
print_r($matches);
Regex demo: https://regex101.com/r/1hKzvM/1/
PHP demo: https://eval.in/784879
Solution without using regex:
$string = '*nJohn*sSmith*fGeorge#*nHenry*sFord';
$result = array();
$persons = explode('#', $string);
foreach ($persons as $person) {
$identials = explode('*', $person);
unset($r);
foreach ($identials as $idential) {
if(!$idential){
continue; //empty string
}
switch ($idential[0]) { //first character
case 'n':
$key = 'name';
break;
case 's':
$key = 'surename';
break;
case 'f':
$key = 'fathers';
break;
}
$r[$key] = substr($idential, 1);
}
$result[] = $r;
}
This function will produce the result that you want ! but consider it's not the only way and not the 100% correct way ! i used preg_split as u asked
function splitMyString($str){
$array_names = [];
$mainString = explode('#', $str);
$arr1 = preg_split("/\*[a-z]/", $mainString[0]);
unset($arr1[0]);
$arr1_values = array_values($arr1);
$arr1_keys = ['name','surname','fathers'];
$result1 = array_combine($arr1_keys, $arr1_values);
// second part of string
$arr2 = preg_split("/\*[a-z]/", $mainString[1]);
unset($arr2[0]);
$arr2_values = array_values($arr2);
$arr2_keys = ['name','surname'];
$arr2 = array_combine($arr2_keys, $arr2_values);
$array_names[] = $arr1;
$array_names[] = $arr2;
return $array_names;
}
// test result !
print_r(splitMyString("*nJohn*sSmith*fGeorge#*nHenry*sFord"));
thanks to all!
For some reason the site blocks my voting for some 'reputation' reason which I find not-fully democracy compliant! On the other hand who cares about democracy these days!
Nevertheless I am using solution #2, without indicating that solution 1 or 3 are not great!
Regards.
However inspired by your answers I came up with mine also, here it is!
$string = '*nJohn*sSmith*fGeorge#*nHenry*sFord';
split_to_key ( $string, array('n'=>'Name','s'=>'Surname','f'=>'Middle'));
function split_to_key ( $string,$ind=array() )
{
$far=null;
$i=0;
$fbig=preg_split('/#/',$string,-1,PREG_SPLIT_NO_EMPTY);
foreach ( $fbig as $fsmall ) {
$f=preg_split('/\*/u',$fsmall,-1,PREG_SPLIT_NO_EMPTY);
foreach ( $f as $fs ) {
foreach( array_keys($ind) as $key ) {
if( preg_match ('/^'.$key.'/u',$fs ) ) {
$fs=preg_replace('/^'.$key.'/u','',$fs);
$far[$i][$ind[$key]]=$fs;
}
}
}
$i++;
}
print_r($far);
}
Like Chris, I wouldn't use preg_split(). My method uses just one preg() function and one loop to completely prepare the filtered output in your desired format (notice my output is 0-indexed, though).
Input (I extended your input sample for testing):
$string='*nJohn*sSmith*fGeorge#*nHenry*sFord#*nJames*sWashington#*nMary*sMiller*fRichard';
Method (PHP Demo & Regex Demo):
if(preg_match_all('/\*n([^*]*)\*s([^*]*)(?:\*f([^#]*))?(?=#|$)/', $string, $out)){
$out=array_slice($out,1); // /prepare for array_column()
foreach($out[0] as $i=>$v){
$name_array[$i]=array_combine(['name','surname','father'],array_column($out,$i));
if($name_array[$i]['father']==''){unset($name_array[$i]['father']);}
}
}
var_export($name_array);
Output:
array (
0 =>
array (
'name' => 'John',
'surname' => 'Smith',
'father' => 'George',
),
1 =>
array (
'name' => 'Henry',
'surname' => 'Ford',
),
2 =>
array (
'name' => 'James',
'surname' => 'Washington',
),
3 =>
array (
'name' => 'Mary',
'surname' => 'Miller',
'father' => 'Richard',
),
)
My regex pattern is optimized for speed by using "negative character classes". I elected to not use the named capture groups because they nearly double the output array size from preg_match_all() and that array requires further preparation anyhow.

Text replace using array

I guys,
I would like replace a text adding specific links on specific words.
So, I created a dynamic array like this:
$replace = array (
"ferrari" => 'ferrari',
"ferrari 2" => 'ferrari 2'
etc.
)
I found a way to replace the text using:
preg_replace(array_keys($replace), array_values($replace), $subject)
but if in the $subject there is the string "ferrari 2" it will be replaced with
[ferrari] instead of [ferrari 2].
How can I do an exact match?
Thanks a lot!
You may use the following solution:
$s = "he has a ferrari and ferrari 2 and what not."; // SAMPLE STRING
$replace = array ( // REPLACEMENTS ARRAY
"ferrari" => 'ferrari',
"ferrari 2" => 'ferrari 2'
);
$keys = array_map('strlen', array_keys($replace)); // SORTING BY KEY LENGTH
array_multisort($keys, SORT_DESC, $replace); // IN DESCENDING ORDER
$pat = '~' . implode("|", array_keys($replace)) . '~'; // BUILDING A PATTERN
echo $pat . "\n"; // => ~ferrari 2|ferrari~ - matches either ferrari 2 or ferrari
$res = preg_replace_callback($pat, function($m) use ($replace) { // REPLACING...
return isset($replace[$m[0]]) ? $replace[$m[0]] : $m[0]; // IF THE KEY EXISTS,
}, $s); // REPLACE WITH VALUE, ELSE KEEP THE MATCH
echo $res; // => he has a ferrari and ferrari 2 and what not.
See the PHP demo
change the order to:
$replace = array (
"ferrari 2" => 'ferrari 2'
"ferrari" => 'ferrari',
)
Usually when you replace, just changing the order to where the smallest keys are last solves a lot.
I imagine that the var $subject is like this:
$subject = 'ferrari ferrari 2';
Try with this:
$replace = array (
"/ferrari(?!\s+\d+)/" => 'ferrari',
"/ferrari 2/" => 'ferrari 2'
);
echo preg_replace(array_keys($replace), array_values($replace), $subject);

Replace variables from text with corresponding values

Let say I have the following string which I want to send as an email to a customer.
"Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}}."
And I have an array with the values that should be replaced
array(
'Name' => $customerName, //or string
'Service' => $serviceName, //or string
'Date' => '2015-06-06'
);
I can find all strings between {{..}} with this:
preg_match_all('/\{{(.*?)\}}/',$a,$match);
where $match is an array with values, But I need to replace every match with a corresponding value from an array with values
Note that the array with values contains a lot more values and the number of items in it or the keys sequence is not relevant to number of matches in string.
You can use preg_replace_callback and pass the array with the help of use to the callback function:
$s = "Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}} {{I_DONT_KNOW_IT}}.";
$arr = array(
'Name' => "customerName", //or string
'Service' => "serviceName", //or string
'Date' => '2015-06-06'
);
echo $res = preg_replace_callback('/{{(.*?)}}/', function($m) use ($arr) {
return isset($arr[$m[1]]) ? $arr[$m[1]] : $m[0]; // If the key is uknown, just use the match value
}, $s);
// => Hello Mr/Mrs customerName. You have subscribed for serviceName at 2015-06-06.
See IDEONE demo.
The $m[1] refers to what has been captured by the (.*?). I guess this pattern is sufficient for the current scenario (does not need unrolling as the strings it matches are relatively short).
You don't need to use a regex for that, you can do it with a simple replacement function if you change a little the array keys:
$corr = array(
'Name' => $customerName, //or string
'Service' => $serviceName, //or string
'Date' => '2015-06-06'
);
$new_keys = array_map(function($i) { return '{{' . $i . '}}';}, array_keys($corr));
$trans = array_combine($new_keys, $corr);
$result = strtr($yourstring, $trans);
Try
<?php
$str = "Hello Mr/Mrs {{Name}}. You have subscribed for {{Service}} at {{Date}}.";
$arr = array(
'Name' => 'some Cust', //or string
'Service' => 'Some Service', //or string
'Date' => '2015-06-06'
);
$replaceKeys = array_map(
function ($el) {
return "{{{$el}}}";
},
array_keys($arr)
);
$str = str_replace($replaceKeys, array_values($arr), $str);
echo $str;

PHP: from string to array: argument => parametr

I have got strings like:
name="n e" content="12" icon="favicon.ico"
What is the best and quickest way to parse it as such array:
Array
(
[name] => "n e"
[content] => "12"
[icon] => "favicon.ico"
)
This should do it, using preg_match_all() to get all the groups and array_combine() to form the final array:
if (preg_match_all('/(\w+)="([^"]*)"/', $str, $matches)) {
return array_combine($matches[1], $matches[2]);
} else {
return array();
}
Edit
This alternative breaks when there are spaces in between the double quotes; otherwise it works as well:
parse_str(str_replace(array(' ', '"'), array('&', ''), $s), $a);
return $a;

Turn text inside brackets to an array PHP

If I have a string that looks like this:
$myString = "[sometext][moretext][993][112]This is a long text";
I want it to be turned into:
$string = "This is a long text";
$arrayDigits[0] = 993;
$arrayDigits[1] = 112;
$arrayText[0] = "sometext";
$arrayText[1] = "moretext";
How can I do this with PHP?
I understand Regular Expressions is the solution. Please notice that $myString was just an example. There can be several brackets, not just two of each, as in my example.
Thanks for your help!
This is what I came up with.
<?php
#For better display
header("Content-Type: text/plain");
#The String
$myString = "[sometext][moretext][993][112]This is a long text";
#Initialize the array
$matches = array();
#Fill it with matches. It would populate $matches[1].
preg_match_all("|\[(.+?)\]|", $myString, $matches);
#Remove anything inside of square brackets, and assign to $string.
$string = preg_replace("|\[.+\]|", "", $myString);
#Display the results.
print_r($matches[1]);
print_r($string);
After that, you can iterate over the $matches array and check each value to assign it to a new array.
Try this:
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\w+)\]/', $s, $m);
$m[1] will contain all texts in the brakets, after this you could check type of each value. Also, you could check this using two preg_match_all: at first time with pattern /\[(\d+)\]/ (will return array of digits), in the second - pattern /\[([a-zA-z]+)\]/ (that will return words):
$s = '[sometext][moretext][993][112]This is a long text';
preg_match_all('/\[(\d+)\]/', $s, $matches);
$arrayOfDigits = $matches[1];
preg_match_all('/\[([a-zA-Z]+)\]/', $s, $matches);
$arrayOfWords = $matches[1];
For cases like yours you can make use of named subpatterns so to "tokenize" your string. With some little code, this can be made easily configurable with an array of tokens:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
Each group contains the subpattern and it's name. They match in their order, so if the group digit matches, it won't give text a chance (which is necessary here because \d+ is a subset of \w+). This array can then turned into a full pattern:
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
The pattern looks like this:
/(?:(?<digit>\[\d+])|(?<text>\[\w+])|(?<free>.+))/
Everything left to do is to execute it against your string, capture the matches and filter them for some normalized output:
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
The matches are now nicely accessible in an array:
Array
(
[digit] => Array
(
[0] => [993]
[1] => [112]
)
[text] => Array
(
[0] => [sometext]
[1] => [moretext]
)
[free] => Array
(
[0] => This is a long text
)
)
The full example at once:
$subject = "[sometext][moretext][993][112]This is a long text";
$groups = array(
'digit' => '\[\d+]',
'text' => '\[\w+]',
'free' => '.+'
);
foreach($groups as $name => &$subpattern)
$subpattern = sprintf('(?<%s>%s)', $name, $subpattern);
unset($subpattern);
$pattern = sprintf('/(?:%s)/', implode('|', $groups));
if (preg_match_all($pattern, $subject, $matches))
{
$matches = array_intersect_key($matches, $groups);
$matches = array_map('array_filter', $matches);
$matches = array_map('array_values', $matches);
print_r($matches);
}
You could try something along the lines of:
<?php
function parseString($string) {
// identify data in brackets
static $pattern = '#(?:\[)([^\[\]]+)(?:\])#';
// result container
$t = array(
'string' => null,
'digits' => array(),
'text' => array(),
);
$t['string'] = preg_replace_callback($pattern, function($m) use(&$t) {
// shove matched string into digits/text groups
$t[is_numeric($m[1]) ? 'digits' : 'text'][] = $m[1];
// remove the brackets from the text
return '';
}, $string);
return $t;
}
$string = "[sometext][moretext][993][112]This is a long text";
$result = parseString($string);
var_dump($result);
/*
$result === array(
"string" => "This is a long text",
"digits" => array(
993,
112,
),
"text" => array(
"sometext",
"moretext",
),
);
*/
(PHP5.3 - using closures)

Categories