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)
Related
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);
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?
I'm trying to replace the characters (numbers and letters) in a string. When I try the "php" way, it gives the wrong result for some of the characters. Why?
PHP-WAY:
$find = array( "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f" );
$replace = array( "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p" );
$haystack = "a5c9a06bfacf5f12cf01ab3f202f6c78"
//This incorrectly returns: kpmjkkglpkmppplmmpklklnpmkmpgmhi
echo str_replace( $find, $replace, $haystack );
LOOP WAY:
$find = array( "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f" );
$replace = array( "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p" );
$haystack = "a5c9a06bfacf5f12cf01ab3f202f6c78"
//This correctly returns: kfmjkaglpkmpfpbcmpabkldpcacpgmhi
$newStr = "";
$chars = str_split( $haystack );
for ( $i = 0, $length = count( $chars ); $i < $length; $i++ )
{
$newStr .= $replace[ array_search( $chars[ $i ], $find ) ];
}
echo $newStr;
Why is the first one incorrect? Am I using it wrong?
Order of entries in your arrays.... str_replace() will process each array entry in the order they appear in your array, so if a '1' gets replaced with 'b', then that 'b' will subsequently get replaced with 'l'; use strtr() rather than str_replace() if you want to prevent that behaviour.
$find = array( "0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f" );
$replace = array( "a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p" );
$haystack = "a5c9a06bfacf5f12cf01ab3f202f6c78" ;
echo strtr($haystack, array_combine($find, $replace));
Your own code only does a single replace because it's looping against your string, not against the from/to arrays.
Just use strtr
$haystack = "a5c9a06bfacf5f12cf01ab3f202f6c78" ;
echo strtr($haystack, implode($find), implode($replace));
Or preg_replace_callback
$find = array_flip($find);
echo preg_replace_callback('/[a-f0-9]/', function ($v) use($replace, $find) {
return $replace[$find[$v[0]]];
}, $haystack);
Output
kfmjkaglpkmpfpbcmpabkldpcacpgmhi
As specified by #MarkBaker, the answer is that str_replace does not simply move forward in the string, but instead works like a recursive .replace(). Instead, use strtr (which is equivalent to Linux tr command:
$tr = array( "0" => "a","1" => "b","2" => "c","3" => "d","4" => "e","5" => "f","6" => "g","7" => "h","8" => "i","9" => "j","a" => "k","b" => "l","c" => "m","d" => "n","e" => "o","f" => "p" );
$haystack = "a5c9a06bfacf5f12cf01ab3f202f6c78"
echo strtr( $haystack, $tr );
I'm using regex to capture the dimensions of ads
Source content is an HTML File, and I'm trying to capture for content that looks like:
size[200x400,300x1200] (could be 1-4 different sizes)
I'm trying to an array with the different sizes in it
My capture code looks like this:
$size_declaration = array();
$sizes = array();
$declaration_pattern = "/size\[(\d{2,4}x\d{2,4}|\d{2,4}x\d{2,4},){1,4}\]/";
$sizes_pattern = "/\d{2,4}x\d{2,4}/";
$result = preg_match($declaration_pattern, $html, $size_declaration);
if( $result ) {
$result = preg_match_all($sizes_pattern, $size_declaration[0], $sizes);
var_dump($sizes);
}
The code above produces usable results:
$sizes = array(
[0] => array (
[0] => '200x400',
[1] => '300x1200'
)
)
but it takes quite a bit of code. I was thinking it was possible to collect the results with a single regex, but I couldn't find a result that works. Is there a way to clean this up a bit?
It's not very practical to turn it into a single expression; it would be better to keep them separate; the first expression finds the boundaries and does rudimentary content checks on the inner contents, the second expression breaks it down into individual pieces:
if (preg_match_all('/size\[([\dx,]+)\]/', $html, $matches)) {
foreach ($matches[0] as $size_declaration) {
if (preg_match_all('/\d+x\d+/', $size_declaration, $sizes)) {
print_r($sizes[0]);
}
}
}
This one is a little simpler:
$html = "size[200x400,300x600,300x100]";
if (($result = preg_match_all("/(\d{2,4}x\d{2,4}){1,4}/", $html, $matches)) > 0)
var_dump($matches);
//
// $matches =>
// array(
// (int) 0 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// ),
// (int) 1 => array(
// (int) 0 => '200x400',
// (int) 1 => '300x600',
// (int) 2 => '300x100'
// )
// )
//
The only way is to repeat the 4 eventual sizes in the pattern:
$subject = <<<LOD
size[523x800]
size[200x400,300x1200]
size[201x300,352x1200,123x456]
size[142x396,1444x32,143x89,231x456]
LOD;
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?(?:,(\d{2,4}x\d{2,4}))?]`';
preg_match_all($pattern, $subject, $matches, PREG_SET_ORDER);
foreach ($matches as &$match) { array_shift($match); }
print_r($matches);
The pattern can also be shorten using references to capture groups:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,((?1)))?(?:,((?1)))?(?:,((?1)))?]`';
or with the Oniguruma syntax:
$pattern = '`size\[(\d{2,4}x\d{2,4})(?:,(\g<1>))?(?:,(\g<1>))?(?:,(\g<1>))?]`';
You can use arrays with str_replace():
$array_from = array ('from1', 'from2');
$array_to = array ('to1', 'to2');
$text = str_replace ($array_from, $array_to, $text);
But what if you have associative array?
$array_from_to = array (
'from1' => 'to1';
'from2' => 'to2';
);
How can you use it with str_replace()?
Speed matters - array is big enough.
$text = strtr($text, $array_from_to)
By the way, that is still a one dimensional "array."
$array_from_to = array (
'from1' => 'to1',
'from2' => 'to2'
);
$text = str_replace(array_keys($array_from_to), $array_from_to, $text);
The to field will ignore the keys in your array. The key function here is array_keys.
$text='yadav+RAHUL(from2';
$array_from_to = array('+' => 'Z1',
'-' => 'Z2',
'&' => 'Z3',
'&&' => 'Z4',
'||' => 'Z5',
'!' => 'Z6',
'(' => 'Z7',
')' => 'Z8',
'[' => 'Z9',
']' => 'Zx1',
'^' => 'Zx2',
'"' => 'Zx3',
'*' => 'Zx4',
'~' => 'Zx5',
'?' => 'Zx6',
':' => 'Zx7',
"'" => 'Zx8');
$text = strtr($text,$array_from_to);
echo $text;
//output is
yadavZ1RAHULZ7from2
$search = array('{user}', '{site}');
$replace = array('Qiao', 'stackoverflow');
$subject = 'Hello {user}, welcome to {site}.';
echo str_replace ($search, $replace, $subject);
Results in Hello Qiao, welcome to stackoverflow..
$array_from_to = array (
'from1' => 'to1',
'from2' => 'to2',
);
This is not a two-dimensional array, it's an associative array.
Expanding on the first example, where we place the $search as the keys of the array, and the $replace as it's values, the code would look like this.
$searchAndReplace = array(
'{user}' => 'Qiao',
'{site}' => 'stackoverflow'
);
$search = array_keys($searchAndReplace);
$replace = array_value($searchAndReplace);
# Our subject is the same as our first example.
echo str_replace ($search, $replace, $subject);
Results in Hello Qiao, welcome to stackoverflow..
$keys = array_keys($array);
$values = array_values($array);
$text = str_replace($key, $values, $string);