I'm trying to create common masks from a string like so:
012abc.d+e_fg~hijk => 012{start}.d+{middle}_fg~{end}jk
replace:
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
Instead I keep overlapping replacements and get something like this instead (using a loop of str_replace's):
012{{pre}art}.d+{mi{post}le}_fg~{end}jk
Because the st is found in the already replaced {start} and dd is found in {middle}.
How would you replace the following?
$str = 'abc.d+e_fg~hijk';
echo replace_vars($str); // Desired output: 012{start}.d+{middle}_fg~{end}kJ
I might misunderstand, but you don't seem to need regex for the replacing. They're simple, literal replacements.
$from = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$to = strtr($from, $arrFromTo); // 012{start}.d+{middle}_fg~{end}jk
strtr() is awesome. It takes a very readable input and it doesn't re-replace like your problem in the loop.
You can use preg_replace like this:
$str = '012abc.d+e_fg~hijk';
$arrFromTo = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$reArr=array();
foreach($arrFromTo as $k=>$v){
$reArr['/' . $k . '(?![^{}]*})/'] = $v;
}
echo preg_replace(array_keys($reArr), array_values($reArr), $str);
//=> 012{start}.d+{middle}_fg~{end}jk
Core of this regex is this negative lookaead: (?![^{}]*})
Which avoid matching keys of array if it is enclosed in {...} since all the replacements are enclosed in {...}.
This will search the string for each replacement in order. If it finds one, it will split the string, and search the remainder of the string for any other replacements.
$str = '012abc.d+e_fg~hijk';
$rep = array(
'st' => '{pre}',
'abc' => '{start}',
'e' => '{middle}',
'hi' => '{end}',
'dd' => '{post}'
);
$searched = '';
foreach ($rep as $key => $r) {
if (strpos($str, $key) !== false) {
$searched .= substr($str, 0, strpos($str, $key)) . $r;
$str = substr($str, strpos($str, $key) + strlen($key));
}
}
$searched .= $str;
echo $searched; //012{start}.d+{middle}_fg~{end}jk
It will search and find them in the order that you have specified.
Related
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;
I have an array like this:
$temp = array( '123' => array( '456' => array( '789' => '0' ) ),
'abc' => array( 'def' => array( 'ghi' => 'jkl' ) )
);
I have a string like this:
$address = '123_456_789';
Can I get value of $temp['123']['456']['789'] using above array $temp and string $address?
Is there any way to achieve this and is it good practice to use it?
This is a simple function that accepts an array and a string address where the keys are separated by any defined delimiter. With this approach, we can use a for-loop to iterate to the desired depth of the array, as shown below.
<?php
function delimitArray($array, $address, $delimiter="_") {
$address = explode($delimiter, $address);
$num_args = count($address);
$val = $array;
for ( $i = 0; $i < $num_args; $i++ ) {
// every iteration brings us closer to the truth
$val = $val[$address[$i]];
}
return $val;
}
$temp = array("123"=>array("456"=>array("789"=>"hello world")));
$address = "123_456_789";
echo delimitArray($temp,$address,"_");
?>
Hello if string $address = '123_456_789'; is your case then you can use explode function to split the string by using some delimeter and you can output your value
<?php
$temp = array('123' => array('456' => array('789' => '0')),
'abc' => array('def' => array('ghi' => 'jkl')),
);
$address = '123_456_789';
$addr = explode("_", $address);
echo $temp[$addr[0]][$addr[1]][$addr[2]];
Using this array library you can easily get element value by either converting your string to array of keys using explode:
Arr::get($temp, explode('_', $address))
or replacing _ with . to take advantage of dot notation access
Arr::get($temp, str_replace('_', '.', $address))
Another benefit of using this method is that you can set default fallback value to return if element with given keys does not exists in array.
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 creating a "quote database" for a TV show I'm a fan of, and I'm rewriting parts of it I don't particularly like. I came across my function to parse the data holding the quote and characters into an array that I can easily loop through and display. One of the features of the site is that you can have a single quote (one-liner) or a conversation between several characters. Right now I'm storing single quotes like this:
[charactername]This is my witty one-liner.
And conversations follow the same pattern:
[characternameone]How's the weather?
[characternametwo]Pretty good, actually.
And so on. Here's the aforementioned parsing function:
function parse_quote($text)
{
// Determine if it's a single or convo
if ( strpos($text, "\n") != false )
{
// Convo
// Let's explode into the separate characters/lines
$text = explode("\n", $text);
$convo = array();
// Parse each line into character and line
foreach ( $text as $part )
{
$character = substr($part, 1, strpos($part, ']') - 1);
$line = substr($part, strlen($character) + 2);
$convo[] = array(
'character' => $character,
'line' => $line
);
}
return array(
'type' => 'convo',
'quote' => $convo
);
}
else
{
// Single
// Parse line into character and line
return array(
'type' => 'single',
'quote' => array(
'character' => substr($text, 1, strpos($text, ']') - 1),
'line' => substr($text, strlen(substr($text, 1, strpos($text, ']') - 1)) + 2)
)
);
}
}
It works as expected, but I can't help but think there's a better way to do this. I'm horrible with regular expressions, which I assume would come in at least somewhat handy in this situation. Any advice, or improvements?
Personally, I would change your data storage method. It would be much easier to deal with a serialized or JSON encoded string.
Instead of
[characternameone]How's the weather?
[characternametwo]Pretty good, actually.
you would have
array(
[0] => {
'name' => "characternameone",
'quote' => "How's the weather?"
},
[1] => {
'name' => "characternametwo",
'quote' => "Pretty good, actually"
}
)
Then when you read it out, there isn't any parsing.
function display_quote($input)
{
for ($i=0, $n=count($input); $i<$n; $i++) {
$quote = $input[$i];
if ( $i > 0 ) echo "\n";
echo $quote['name'] . ': ' . $quote['quote'];
}
}
Instead of
$character = substr($part, 1, strpos($part, ']') - 1);
$line = substr($part, strlen($character) + 2);
$convo[] = array(
'character' => $character,
'line' => $line
);
you could try
preg_match('#\[([^\]]+)\](.*)#ism', $part, $match);
$convo[] = array(
'character' => $match[1],
'line' => $match[2]
);
HTH
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);