Currently, I have the following code, which use to perform pattern matching & pattern replacing.
<?php
function fun($text) {
if (preg_match("/^(ATE|EAT)\s+(\S+).*/", $text)) {
$text = preg_replace("/^(ATE|EAT)\s+(\S+).*/", "EAT $2", $text);
} else {
$text = preg_replace("/.*(ATE|EAT).*/", "EAT", $text);
}
return $text;
}
// EAT pizza
echo fun('EAT pizza') . "\n";
// EAT pizza
echo fun('EAT pizza at paris') . "\n";
// EAT
echo fun('I EAT pizza at paris') . "\n";
I feel the above code is not optimized, as I'm performing preg_match before preg_replace. I feel this is redundant.
I tried to check the returned string from preg_replace
<?php
function fun($text) {
$tmp = preg_replace("/^(ATE|EAT)\s+(\S+).*/", "EAT $2", $text);
// I don't think this is the correct way to check for "Not Match" ?!
if ($tmp === $text) {
$tmp = preg_replace("/.*(ATE|EAT).*/", "EAT", $text);
}
return $tmp;
}
// EAT pizza
echo fun('EAT pizza') . "\n";
// EAT pizza
echo fun('EAT pizza at paris') . "\n";
// EAT
echo fun('I EAT pizza at paris') . "\n";
However, fun('EAT pizza') will fail, as it will print out EAT instead of expected EAT pizza.
Is there a correct way to optimize the above code?
I assume all you actually ask for is to know how to know if there where any replacements in your preg_replace since it is quite unclear what you actually want to achieve.
From the PHP Documentation on preg-replace:
Description
mixed preg_replace ( mixed $pattern , mixed $replacement , mixed $subject [, int $limit = -1 [, int &$count ]] )
[..]
count
If specified, this variable will be filled with the number of replacements done.
So if you add a count to your preg_replace, you'll get the number of replacements, thus the number of occurances.
Example given (thou this is kind of into the blue due to lack of information):
function fun($text) {
$count = 0;
$tmp = preg_replace("/^(ATE|EAT)\s+(\S+).*/", "EAT $2", $text, -1, $count);
if ($count == 0) {
$tmp = preg_replace("/.*(ATE|EAT).*/", "EAT", $text);
}
return $tmp;
}
So this will replace ATE <something> or EAT <something> with EAT <something>, or, if neither of those where found, it will attempt to replace ATE or EAT with EAT.
Try with following function:
function fun($text) {
$tmp = str_replace('ATE', 'EAT', $text);
$pos = strpos($text, 'EAT');
if ($pos < 0) {
return $text;
} else if ($pos > 0) {
$tmp = preg_replace('/^.*?EAT/', 'EAT', $tmp);
}
$tmp = preg_replace("/^EAT\s+(\S+).*/", "EAT $2", $tmp);
return $tmp;
}
Related
I have a search String: $str (Something like "test"), a wrap string: $wrap (Something like "|") and a text string: $text (Something like "This is a test Text").
$str is 1 Time in $text. What i want now is a function that will wrap $str with the wrap defined in $wrap and output the modified text (even if $str is more than one time in $text).
But it shall not output the whole text but just 1-2 of the words before $str and then 1-2 of the words after $str and "..." (Only if it isn`t the first or last word). Also it should be case insensitive.
Example:
$str = "Text"
$wrap = "<span>|</span>"
$text = "This is a really long Text where the word Text appears about 3 times Text"
Output would be:
"...long <span>Text</span> where...word <span>Text</span> appears...times <span>Text</span>"
My Code (Obviusly doesnt works):
$tempar = preg_split("/$str/i", $text);
if (count($tempar) <= 2) {
$result = "... ".substr($tempar[0], -7).$wrap.substr($tempar[1], 7)." ...";
} else {
$amount = substr_count($text, $str);
for ($i = 0; $i < $amount; $i++) {
$result = $result.".. ".substr($tempar[$i], -7).$wrap.substr($tempar[$i+1], 0, 7)." ..";
}
}
If you have a tipp or a solution dont hesitate to let me know.
I have taken your approach and made it more flexible. If $str or $wrap changes you could have escaping issues within the regex pattern so I have used preg_quote.
Note that I added $placeholder to make it clearer, but you can use $placeholder = "|" if you don't like [placeholder].
function wrapInString($str, $text, $element = 'span') {
$placeholder = "[placeholder]"; // The string that will be replaced by $str
$wrap = "<{$element}>{$placeholder}</{$element}>"; // Dynamic string that can handle more than just span
$strExp = preg_quote($str, '/');
$matches = [];
$matchCount = preg_match_all("/(\w+\s+)?(\w+\s+)?({$strExp})(\s+\w+)?(\s+\w+)?/i", $text, $matches);
$response = '';
for ($i = 0; $i < $matchCount; $i++) {
if (strlen($matches[1][$i])) {
$response .= '...';
}
if (strlen($matches[2][$i])) {
$response .= $matches[2][$i];
}
$response .= str_replace($placeholder, $matches[3][$i], $wrap);
if (strlen($matches[4][$i])) {
$response .= $matches[4][$i];
}
if (strlen($matches[5][$i]) && $i == $matchCount - 1) {
$response .= '...';
}
}
return $response;
}
$text = "text This is a really long Text where the word Text appears about 3 times Text";
string(107) "<span>text</span> This...long <span>text</span> where...<span>text</span> appears...times <span>text</span>"
To make the replacement case insensitive you can use the i regex option.
If I understand your question correct, just a little bit of implode and explode magic needed
$text = "This is a really long Text where the word Text appears about 3 times Text";
$arr = explode("Text", $text);
print_r(implode('<span>Text</span>', $arr));
If you specifically need to render the span tags using HTML, just write it that way
$arr = explode("Text", $text);
print_r(implode('<span>Text</span>', $arr));
Use patern below to get your word and 1-2 words before and after
/((\w+\s+){1,2}|^)text((\s+\w+){1,2}|$)/i
demo
In PHP code it can be:
$str = "Text";
$wrap = "<span>|</span>";
$text = "This is a really long Text where the word Text appears about 3 times Text";
$temp = str_replace('|', $str, $wrap); // <span>Text</span>
// find patern and 1-2 words before and after
// (to make it casesensitive, delete 'i' from patern)
if(preg_match_all('/((\w+\s+){1,2}|^)text((\s+\w+){1,2}|$)/i', $text, $match)) {
$res = array_map(function($x) use($str, $temp) { return '... '.str_replace($str, $temp, $x) . ' ...';}, $match[0]);
echo implode(' ', $res);
}
I want to ask about is it possible to get some string that between a specifi keyword? For example, I have 2 sentence like these:
I will go to #new bathroom and wash the car#
Result: bathroom and wash the car
Someone need an #new icebreaker# to hold that problem
Result : icebreaker
I want to make condition to get all words between the #new #
Any idea how to create this?
My code so far:
<?php
$sentence = "I will go to #new bathroom and wash the car#";
$start = strpos($sentence, "#new");
//$end = strpos($sentence, "#");
$end = 20; //because my strpos still wrong, I define a static number
$new = substr($sentence, $start, $end);
echo $new;
?>
My problem is I can't find a way to chase the last hashtag
Use this regular expression:
/#new (.+)#/i
Together with preg_match(), you'll get your match in an array:
<?php
$string = "Someone need an #new icebreaker# to hold that problem";
preg_match("/#new (.+)#/i", $string, $matches);
var_dump($matches[1]); // icebreaker
Demo
If you anticipate more than one possible match, use preg_match_all() to get them all.
I have written the following code for your problem but please bare in mind that i am still a beginner myself.
It works exactly how you want it to but i am sure there are better solutions out there.
<?php
$string = "I will go to #new bathroom and wash the car#";
$stringArray = str_split($string);
$output = '';
$count = 0;
foreach($stringArray as $letter){
if($count == 0 && $letter == '#'){
$count = 1;
} elseif($count == 1){
if($letter != '#'){
$output .= $letter;
} else {
$count = 2;
}
}
}
echo $output;
?>
hope this helps :)
Another way different to Regular Expression is to explode the string and replace the new in the sentence
This will just work if you have only one keyword in the sentence #new
$string = "I will go to #new bathroom and wash the car#";
$string1 = "Someone need an #new icebreaker# to hold that problem";
function getString($string, $delimiter = '#')
{
$string_array = explode($delimiter, $string);
return str_replace('new ', '', $string_array[1]);
}
echo getString($string);
//bathroom and wash the car
echo getString($string1);
//icebreaker
I'd like more work with arrays
$string = [
"I will go to #new bathroom and wash the car#",
"Someone need an #new icebreaker# to hold that problem"
];
function getString($string, $delimiter = '#')
{
$result = [];
foreach ($string as $value) {
$string_array = strstr($value, $delimiter) ? explode($delimiter, $value) : [];
$result[] = isset($string_array[1]) ? str_replace('new ', '', $string_array[1]) : NULL;
}
return $result;
}
print_r(getString($string));
/*
Array
(
[0] => bathroom and wash the car
[1] => icebreaker
)
*/
You can use regex to match that. here is a links and a simple regex.
(#)\w+(#)
(\#)+(.)+(\#)
https://regexr.com/
http://php.net/manual/en/function.preg-match.php
You can search "#" from end,
like $end = strpos($sentence, "#", -0);
and than get substring as you already have.
$new = substr($sentence, $start, $end);
I want to replace some numbers in a string with the content of the array in the position which this number points to.
For example, replace "Hello 1 you are great", with "Hello myarray[1] you are great"
I was doing the next: preg_replace('/(\d+)/','VALUE: ' . $array[$1],$string);
But it does not work. How could I do it?
You should use a callback.
<?php
$str = 'Hello, 1!';
$replacements = array(
1 => 'world'
);
$str = preg_replace_callback('/(\d+)/', function($matches) use($replacements) {
if (array_key_exists($matches[0], $replacements)) {
return $replacements[$matches[0]];
} else {
return $matches[0];
}
}, $str);
var_dump($str); // 'Hello, world!'
Since you are using a callback, in the event that you actually want to use a number, you might want to encode your strings as {1} or something instead of 1. You can use a modified match pattern:
<?php
// added braces to match
$str = 'Hello, {1}!';
$replacements = array(
1 => 'world'
);
// added braces to regex
$str = preg_replace_callback('/\{(\d+)\}/', function($matches) use($replacements) {
if (array_key_exists($matches[1], $replacements)) {
return $replacements[$matches[1]];
} else {
// leave string as-is, with braces
return $matches[0];
}
}, $str);
var_dump($str); // 'Hello, world!'
However, if you are always matching known strings, you may want to use #ChrisCooney's solution because it offers less opportunity to screw up the logic.
The other answer is perfectly fine. I managed it this way:
$val = "Chris is 0";
// Initialise with index.
$adj = array("Fun", "Awesome", "Stupid");
// Create array of replacements.
$pattern = '!\d+!';
// Create regular expression.
preg_match($pattern, $val, $matches);
// Get matches with the regular expression.
echo preg_replace($pattern, $adj[$matches[0]], $val);
// Replace number with first match found.
Just offering another solution to the problem :)
$string = "Hello 1 you are great";
$replacements = array(1 => 'I think');
preg_match('/\s(\d)\s/', $string, $matches);
foreach($matches as $key => $match) {
// skip full pattern match
if(!$key) {
continue;
}
$string = str_replace($match, $replacements[$match], $string);
}
<?php
$array = array( 2 => '**', 3 => '***');
$string = 'lets test for number 2 and see 3 the result';
echo preg_replace_callback('/(\d+)/', 'replaceNumber', $string);
function replaceNumber($matches){
global $array;
return $array[$matches[0]];
}
?>
output
lets test for number ** and see *** the result
I need some help i have this code that Uppercase the first character of each word in a string with exceptions i need the function to ignore the exception if it's at the beginning of the string:
function ucwordss($str, $exceptions) {
$out = "";
foreach (explode(" ", $str) as $word) {
$out .= (!in_array($word, $exceptions)) ? strtoupper($word{0}) . substr($word, 1) . " " : $word . " ";
}
return rtrim($out);
}
$string = "my cat is going to the vet";
$ignore = array("is", "to", "the");
echo ucwordss($string, $ignore);
// Prints: My Cat is Going to the Vet
this is what im doing:
$string = "my cat is going to the vet";
$ignore = array("my", "is", "to", "the");
echo ucwordss($string, $ignore);
// Prints: my Cat is Going to the Vet
// NEED TO PRINT: My Cat is Going to the Vet
- return rtrim($out);
+ return ucfirst(rtrim($out));
Something like this:
function ucwordss($str, $exceptions) {
$out = "";
foreach (explode(" ", $str) as $key => $word) {
$out .= (!in_array($word, $exceptions) || $key == 0) ? strtoupper($word{0}) . substr($word, 1) . " " : $word . " ";
}
return rtrim($out);
}
Or even easier, before return in your function make strtoupper first letter
Do this really cheaply by just always uppercasing your first word:
function ucword($word){
return strtoupper($word{0}) . substr($word, 1) . " ";
}
function ucwordss($str, $exceptions) {
$out = "";
$words = explode(" ", $str);
$words[0] = ucword($words[0]);
foreach ($words as $word) {
$out .= (!in_array($word, $exceptions)) ? ucword($word) : $word . " ";
}
return rtrim($out);
}
what about you make the first letter in the string upper case so no matter your mix you will still come through
$string = "my cat is going to the vet";
$string = ucfirst($string);
$ignore = array("is", "to", "the");
echo ucwordss($string, $ignore);
this way you first letter of the string will always be upper case
preg_replace_callback() will allow you to express your conditional replacement logic in a loopless and dynamic fashion. Consider this approach that will suitably modify your sample data:
Code: (PHP Demo) (Pattern Demo)
$string = "my cat is going to the vet";
$ignore = array("my", "is", "to", "the");
$pattern = "~^[a-z]+|\b(?|" . implode("|", $ignore) . ")\b(*SKIP)(*FAIL)|[a-z]+~";
echo "$pattern\n---\n";
echo preg_replace_callback($pattern, function($m) {return ucfirst($m[0]);}, $string);
Output:
~^[a-z]+|\b(?|my|is|to|the)\b(*SKIP)(*FAIL)|[a-z]+~
---
My Cat is Going to the Vet
You see, the three piped portions of the pattern (in order) make these demands:
If the start of the string is a word, capitalize the first letter.
If a "whole word" (leveraging the \b word boundary metacharacter) is found in the "blacklist", disqualify the match and keep traversing the input string.
Else capitalize the first letter of every word.
Now, if you want to get particular about contractions and hyphenated words, then you only need to add ' and - to the [a-z] character classes like this: [a-z'-] (Pattern Demo)
If anyone has a fringe cases that will break my snippet (like "words" with special characters that need to be escaped by preg_quote()), you can offer them and I can offer a patch, but my original solution will adequately serve the posted question.
I need to convert lib_someString to someString inside a block of text using str_replace [not regex].
Here's an example to give an exact sense what I mean: lib_12345 => 12345. I need to do this for a bunch of instances in a block of text.
Below is my attempt. Problem I'm getting is that my function is not doing anything (I just get lib_id returned).
function extractLibId($val){ // function to get the "12345" in the above example
$lclRetVal = substr($val, 5, strlen($val));
return $lclRetVal;
}
function Lib($text){ // does the replace for all lib_ instances in the text
$lclVar = "lib_";
$text = str_replace($lclVar, "<a href='".extractLibId($lclVar)."'>".extractLibId($lclVar)."</a>", $text);
return $text;
}
Regexp gonna be faster and more clear, you will have no need to call your function for every possible 'lib_' string:
function Lib($text) {
$count = null;
return preg_replace('/lib_([0-9]+)/', '$1', $text, -1, $count);
}
$text = 'some text lib_123123 goes here lib_111';
$text = Lib($text);
Without regexp, but every time Lib2 will be called somewhere will die cute kitten:
function extractLibId($val) {
$lclRetVal = substr($val, 4);
return $lclRetVal;
}
function Lib2($text) {
$count = null;
while (($pos = strpos($text, 'lib_')) !== false) {
$end = $pos;
while (!in_array($text[$end], array(' ', ',', '.')) && $end < strlen($text))
$end++;
$sub = substr($text, $pos, $end - $pos);
$text = str_replace($sub, ''.extractLibId($sub).'', $text);
}
return $text;
}
$text = 'some text lib_123123 goes here lib_111';
$text = Lib2($text);
Use preg_replace.
Although it is possible to do what you need without regular expressions, you say you don't want to use them because of performance reasons. I doubt the other solution will be faster, so here is a simple regex to benchmark against:
echo preg_replace("/lib_(\w+)/", '$1', $str);
As shown here: http://codepad.org/xGj78r9r
Ignoring how ridiculous area of optimizing this is, even the simplest implementation with minimal validation already takes only 33% less time than a regex
<?php
function uselessFunction( $val ) {
if( strpos( $val, "lib_" ) !== 0 ) {
return $val;
}
$str = substr( $val, 4 );
return "{$str}";
}
$l = 100000;
$now = microtime(TRUE);
while( $l-- ) {
preg_replace( '/^lib_(.*)$/', "$1", 'lib_someString' );
}
echo (microtime(TRUE)-$now)."\n";
//0.191093
$l = 100000;
$now = microtime(TRUE);
while( $l-- ) {
uselessFunction( "lib_someString" );
}
echo (microtime(TRUE)-$now);
//0.127598
?>
If you're restricted from using a regex, you're going to have difficult time searching for a string you describe as "someString", i.e. not precisely known in advance. If you know the string is exactly lib_12345, for example, then set $lclVar to that string. On the other hand, if you don't know the exact string in advance, you'll have to use a regex via preg_replace() or a similar function.