str_replace() with foreach, uppercase and exact word matching - php

My code is:
$db = &JFactory::getDBO();
$query = $db->getQuery(true);
$query->select($db->quoteName(array('old', 'new')))
->from($db->quoteName('#__words'));
$db->setQuery($query);
$results = $db->loadAssocList();
$find = array();
$replace = array();
foreach ($results as $row) {
$find[] = $row['old'];
$replace[] = $row['new'];
}
$newstring = str_replace($find, $replace, $oldstring);
echo $newstring;
This code replaces all the words from the "old" column with the words from the column "new". But there are two problems. First it works if the found and replaced words both have the same case (upper or lower), but I need it would be working regardless of the case i.e. the DB has only in lowercase but if the finding word on the front-end have uppercase, the replaced word must have uppercase too. Secondly, I need exact word matching. Thanks in advance!

Try something like this:
$oldstring = 'The cat and the dog are flying into the kitchen. Dog. Cat. DOG. CAT';
$results = array( array('old'=>'cat', 'new'=>'chat'), array('old'=>'dog', 'new'=>'chien'));
foreach ($results as $row) {
$fndrep[$row['old']] = $row['new'];
}
$pattern = '~(?=([A-Z]?)([a-z]?))\b(?i)(?:'
// cyrillic => '~(?=([\x{0410}-\x{042F}]?)([\x{0430}-\x{044F}]?))\b(?i)(?:'
. implode('|', array_keys($fndrep))
. ')\b~'; // cyrillic => ')\b~u';
$newstring = preg_replace_callback($pattern, function ($m) use ($fndrep) {
$lowm = $fndrep[strtolower($m[0])];
if ($m[1])
return ($m[2]) ? ucfirst($lowm) : strtoupper($lowm);
else
return $lowm;
}, $oldstring);
echo $newstring;

Related

Delete words that match the pattern on the string

I have to filter a string according to words similar to the pattern.
I need to delete words that match the formula:
<?php
$string = 'armin van burren yummy';
$pattern = 'a%n v%n b%n';
//result: Yummy
$string = 'Beyonce - love on top';
$pattern = 'b%e';
//result: Love on top
$string = 'Ed Sheeran - Shape of You';
$pattern = 'e_ s_____n';
//result: Shape of You
?>
Do you have any idea how to get this result, maybe there is some function in php. I tried to search, unfortunately I didn't find any information. Thank you for all the help and examples
This code works for me, is it possible to limit the amount of foreach for this code (performance is concerned)?
function filterlike($arr, $like){
if ($arr && $like){
$like = preg_replace('/_{1,}/','.*', $like);
$like = preg_replace('/%/','.*', $like);
$like = explode(' ', $like);
$filter = array();
foreach ($arr as $a){
foreach ($like as $l){
$a = preg_replace('/'.$l.'/', '', $a);
}
$filter[] = $a;
}
return $filter;
}
}
print_r(filterlike(array('armin', 'van', 'burren', 'jummy'), 'a%n v%n b%n'));
print_r(filterlike(array('armin', 'van', 'burren', 'jummy'), 'a___n v_n b____n'));

How to loop through, match and replace?

I have multiple strings with same curly braces I want to replace them as dynamic if I get the count as 1 then need to replace the first occurrence, If count as 2 then replaces the second occurrence as so on until condition satisfies.
<?php
include_once("con.php");
$db = new Da();
$con = $db->con();
$String = "{{ONE}} {{TWO}} {{THREE}} {{FOUR}} {{FIVE}} {{SIX}}";
$Count = 1;
if(preg_match_all("/\{\{[^{}]+\}\}/", $lclString, $matches)) {
foreach ($matches[0] as $match) {
$Count++;
$Query = "SELECT link FROM student WHERE linkVal = '".$match."'";
$Result = $con->query($Query);
if($row = $Result->fetch(PDO::FETCH_ASSOC)) {
$NewValue = preg_replace("/\{\{[^{}]+\}\}/", $row["link"], $String);
}
}
echo json_encode($NewValue);
}
?>
If first occurrence the {{ONE}} should replace with new value with $row["link"],
Secondly replace {{TWO}} With New value so on.
Within the loop on each match, instead of using preg_replace, I suggest you to use str_replace:
if(preg_match_all("/\{\{[^{}]+\}\}/", $lclString, $matches)) {
$NewValue = $String;
foreach ($matches[0] as $match) {
$Count++;
$Query = "SELECT link FROM student WHERE linkVal = '".$match."'";
$Result = $con->query($Query);
if($row = $Result->fetch(PDO::FETCH_ASSOC)) {
$NewValue = str_replace($match, $row["link"], $NewValue);
// ^^^^^^^^^^^^^^^^^^
}
}
echo json_encode($NewValue);
}
You can greatly simplify your code by fetching all the replacement values in one query:
$String = "{{ONE}} {{TWO}} {{THREE}} {{FOUR}} {{FIVE}} {{SIX}}";
if(preg_match_all("/\{\{[^{}]+\}\}/", $String, $matches)) {
$Query = "SELECT linkVal, link FROM student WHERE linkVal IN('".implode("','", $matches[0])."')";
$Result = $con->query($Query);
if ($rows = $Result->fetchAll(PDO::FETCH_ASSOC)) {
$NewValue = str_replace(array_column($rows, 'linkVal'), array_column($rows, 'link'), $String);
}
echo json_encode($NewValue);
}
You can use preg_replace_callback to iterate through your matches.
e.g.
$str = "{{ONE}} {{TWO}} {{THREE}} {{FOUR}} {{FIVE}} {{SIX}}";
$replaced = preg_replace_callback('/{{([^{}]*)}}/', function($match) {
// $match[1] contains the current match (first capture group) e.g. ONE
// return your replacement for the current match, e.g. reversed string ENO
return strrev($match[1]);
}, $str);
echo $replaced;
Output will be ENO OWT EERHT RUOF EVIF XIS
There are a few issues with your code, you need to ensure that the variable in the preg_match_all() is the string your trying to search.
But the main problem is in the replacement part. You need to replace the current match value ($match) and replace it in a new string - currently you always replace the new match in the original string. Here I create $NewValue from the original string and keep replacing values in that...
if(preg_match_all("/\{\{[^{}]+\}\}/", $String, $matches)) {
$NewValue = $String;
foreach ($matches[0] as $match) {
$Count++;
$Query = "SELECT link FROM student WHERE linkVal = '".$match."'";
$Result = $con->query($Query);
if($row = $Result->fetch(PDO::FETCH_ASSOC)) {
$NewValue = preg_replace("/".preg_quote($match)."/",
$row["link"], $NewValue);
}
}
echo json_encode($NewValue);
}
You should also look into using prepared statements as currently you may be open to SQL Injection problems.
The expression we might wish to design here can be like one of these:
({{)(.+?)(}})
which is only using capturing groups ().
DEMO 1
(?:{{)(.+?)(?:}})
and here we can use non-capturing groups (?:), if we do not wish to keep the {{ and }}.
DEMO 2
Then, we could simply do the preg_replace that we wanted to do.
Test
$re = '/(?:{{)(.+?)(?:}})/m';
$str = '{{ONE}} {{TWO}} {{THREE}} {{FOUR}} {{FIVE}} {{SIX}}';
$subst = '$1';
$result = preg_replace($re, $subst, $str);
echo "The result of the substitution is ".$result;

PHP find tags in content text and wrap in <a> tags and set limit the number of links

Im finding keywords "denounce,and,demoralized" in a string, and wrapping it in "html a" tags to change it to link with following function...
function link2tags($text, $tags){
$tags = preg_replace('/\s+/', ' ', trim($tags));
$words = explode(',', $tags);
$linked = array();
foreach ( $words as $word ){
$linked[] = ''.$word.'';
}
return str_replace($words, $linked, $text);
}
echo link2tags('we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment', 'denounce,and,demoralized');
The output of the above function is as follows...
Output:
we denounce with righteous indignation and dislike men who are so beguiled and demoralized by the charms of pleasure of the moment
Here, the word "and" is linked 2 times I want to limit the number of links to a word
Repeat words are only linked once
You need to get only first occurrence of words and then need to replace those. Check below code:
function link2tags($text, $tags){
$tags = preg_replace('/\s+/', ' ', trim($tags));
$words = explode(',', $tags);
$linked = array();
$existingLinks = array();
foreach ( $words as $word ){
if (!in_array($word, $existingLinks)) {
$existingLinks[] = $word;
$linked[] = ''.$word.'';
}
}
foreach ($existingLinks as $key => $value) {
$text = preg_replace("/".$value."/", $linked[$key], $text, 1);
}
return $text;
}
Hope it helps you.
Here you can check existing word as below:
if(!in_array($word,$alreadyusedword)) {
$linked[] = ''.$word.'';
$alreadyusedword[] = $word;
}

complex regex with preg_replace, replace a word not inside [ and ]

I'm having trouble finding a correct regex to achieve what I want.
I have a sentence like that :
Hi, my name is Stan, you are welcome, hello.
and I would like to transform it like that :
[hi|hello|welcome], my name is [stan|jack] you are [hi|hello|welcome] [hi|hello|welcome].
Right now my regex is half working, because somes words are not replaced, and those replaced are deleting some characters
Here is my test code
<?php
$test = 'Hi, my name is Stan, you are welcome, hello.';
$words = array(
array('hi', 'hello', 'welcome'),
array('stan', 'jack'),
);
$result = $test;
foreach ($words as $group) {
if (count($group) > 0) {
$replacement = '[' . implode('|', $group) . ']';
foreach ($group as $word) {
$result = preg_replace('#([^\[])' . $word . '([^\]])#i', $replacement, $result);
}
}
}
echo $test . '<br />' . $result;
Any help will be appreciated
The regex you are using is overcomplicated. You simply need to use a regex substitution using regular brackets ():
<?php
$test = 'Hi, my name is Stan, you are welcome, hello.';
$words = array(
array('hi', 'hello', 'welcome'),
array('stan', 'jack'),
);
$result = $test;
foreach ($words as $group) {
if (count($group) > 0) {
$imploded = implode('|', $group);
$replacement = "[$imploded]";
$search = "($imploded)";
$result = preg_replace("/$search/i", $replacement, $result);
}
}
echo $test . '<br />' . $result;
Your regular expression:
'#([^\[])' . $word . '([^\]])#i'
matches one character before and after $word as well. And as they do, they replace it. So your replacement string needs to reference these parts, too:
'$1' . $replacement . '$2'
Demo
preg_replace supports array as parameter. No need to iterate with a loop.
$s = array("/(hi|hello|welcome)/i", "/(stan|jack)/i");
$r = array("[hi|hello|welcome]", "[stan|jack]");
preg_replace($s, $r, $str);
or dynamically
$test = 'Hi, my name is Stan, you are welcome, hello.';
$s = array("hi|hello|welcome", "stan|jack");
$r = array_map(create_function('$a','return "[$a]";'), $s);
$s = array_map(create_function('$a','return "/($a)/i";'), $s);
echo preg_replace($s, $r, $str);
//[hi|hello|welcome], my name is [stan|jack], you are [hi|hello|welcome], [hi|hello|welcome].

Uppercase the first character of each word in a string except 'and', 'to', etc

How can I make upper-case the first character of each word in a string accept a couple of words which I don't want to transform them, like - and, to, etc?
For instance, I want this - ucwords('art and design') to output the string below,
'Art and Design'
is it possible to be like - strip_tags($text, '<p><a>') which we allow and in the string?
or I should use something else? please advise!
thanks.
None of these are really UTF8 friendly, so here's one that works flawlessly (so far)
function titleCase($string, $delimiters = array(" ", "-", ".", "'", "O'", "Mc"), $exceptions = array("and", "to", "of", "das", "dos", "I", "II", "III", "IV", "V", "VI"))
{
/*
* Exceptions in lower case are words you don't want converted
* Exceptions all in upper case are any words you don't want converted to title case
* but should be converted to upper case, e.g.:
* king henry viii or king henry Viii should be King Henry VIII
*/
$string = mb_convert_case($string, MB_CASE_TITLE, "UTF-8");
foreach ($delimiters as $dlnr => $delimiter) {
$words = explode($delimiter, $string);
$newwords = array();
foreach ($words as $wordnr => $word) {
if (in_array(mb_strtoupper($word, "UTF-8"), $exceptions)) {
// check exceptions list for any words that should be in upper case
$word = mb_strtoupper($word, "UTF-8");
} elseif (in_array(mb_strtolower($word, "UTF-8"), $exceptions)) {
// check exceptions list for any words that should be in upper case
$word = mb_strtolower($word, "UTF-8");
} elseif (!in_array($word, $exceptions)) {
// convert to uppercase (non-utf8 only)
$word = ucfirst($word);
}
array_push($newwords, $word);
}
$string = join($delimiter, $newwords);
}//foreach
return $string;
}
Usage:
$s = 'SÃO JOÃO DOS SANTOS';
$v = titleCase($s); // 'São João dos Santos'
since we all love regexps, an alternative, that also works with interpunction (unlike the explode(" ",...) solution)
$newString = preg_replace_callback("/[a-zA-Z]+/",'ucfirst_some',$string);
function ucfirst_some($match)
{
$exclude = array('and','not');
if ( in_array(strtolower($match[0]),$exclude) ) return $match[0];
return ucfirst($match[0]);
}
edit added strtolower(), or "Not" would remain "Not".
How about this ?
$string = str_replace(' And ', ' and ', ucwords($string));
You will have to use ucfirst and loop through every word, checking e.g. an array of exceptions for each one.
Something like the following:
$exclude = array('and', 'not');
$words = explode(' ', $string);
foreach($words as $key => $word) {
if(in_array($word, $exclude)) {
continue;
}
$words[$key] = ucfirst($word);
}
$newString = implode(' ', $words);
I know it is a few years after the question, but I was looking for an answer to the insuring proper English in the titles of a CMS I am programming and wrote a light weight function from the ideas on this page so I thought I would share it:
function makeTitle($title){
$str = ucwords($title);
$exclude = 'a,an,the,for,and,nor,but,or,yet,so,such,as,at,around,by,after,along,for,from,of,on,to,with,without';
$excluded = explode(",",$exclude);
foreach($excluded as $noCap){$str = str_replace(ucwords($noCap),strtolower($noCap),$str);}
return ucfirst($str);
}
The excluded list was found at:
http://www.superheronation.com/2011/08/16/words-that-should-not-be-capitalized-in-titles/
USAGE: makeTitle($title);

Categories