for example I have following string:
a_b__c___d____e
How to preg_replace char _ to char '-', but only if part ' __...' contains more than N repeated _.
I hope you understand me ))
source: a_b__c___d____e
cond: change '_' where 2 or more
result: a_b--c---d----e
or
source: a_b__c___d____e_____f
cont: change '_' where 4 or more
result: a_b__c___d----e-----f
Thanks!
p.s. Interesting solution without using loops. How implement it with loops (I think) know anybody. Just a one regex and preg_replace.
Here is another one using the e modifier:
$str = 'a_b__c___d____e_____f';
echo preg_replace('/_{4,}/e', 'str_repeat("-", strlen("$0"))', $str);
Replace 4 by the number you need. Or as function:
function repl($str, $char, $times) {
$char = preg_quote($char, '/');
$times = preg_quote($times, '/');
$pattern = '/' . $char . '{' . $times . ',}/e',
return preg_replace($pattern, 'str_repeat("-", strlen("$0"))', $str);
}
$source = 'a_b__c___d____e_____f';
function yourfunc($param)
{
$count = strlen($param);
$return = '';
for ($i = 0; $i < $count; $i++)
{
$return .= '-';
}
return $return;
}
echo preg_replace('#(_{4,})#e', 'yourfunc("$1");', $source);
A solution without callback function and loop is much harder to read.
preg_replace('#(_{4,})#e', 'implode("", array_pad(array(), strlen("$1"), "-"));', $source);
this is inline solution :
preg_replace('/(_{2,})/ie', 'str_repeat("-",strlen("$1"));', $source);
and reusable funciton:
$source = 'a_b__c___d____e_____f';
function replace_repeatable($source,$char,$replacement,$minrepeat = 2)
{
return preg_replace('/(' . preg_quote($char) . '{' . $minrepeat . ',})/ie', 'str_repeat("' . $replacement . '",strlen("$1"));', $source);
}
$b = replace_repeatable($source,'_','-',4);
As referring to php.net documenation using modifier e is discouraged,
This feature has been DEPRECATED as of PHP 5.5.0. Relying on this feature is highly discouraged.
so we'd better to achieve our goal without using this modifier.
Here's solution based on up to date PHP's tools:
$source = 'a_b__c___d____e';
echo preg_replace_callback( "%(_{2,})%i", function($matches) {return str_repeat( "-", strlen($matches[1]) ); }, $source );
/* in callback function matches[0] is whole matched pattern, groups go like this matches[1],matches[2]... */
Even with e still available in our PHP environment, it is generally better to use callback function - thank's to callback we avoid rather unsafe combination of addslashes() function and string evaluation, since running preg_replace with mentioned modifier engages both actions at a time.
A preg_replace_callback has been available since version 4.0.5, but function($matches) {} is an anonymous function which is actually much newer language feature, to run this code u need PHP in version 5.3.0 or newer.
You can replace the dashes one by one using the \G anchor to ensure a contiguity from the position of the first - (followed by n-1 other -) to the last one. This way you only have to check the number of following dashes after the first one:
echo preg_replace('~\G(?!^)_|_(?=_)~', '-', $str);
demo
for n=2:
\G(?!^)_|_(?=_)
for n=3:
\G(?!^)_|_(?=_{2})
for n=4:
\G(?!^)_|_(?=_{3})
etc.
The first branch \G(?!^)_ succeeds only when there's a successfull match at the previous position. In other words, that means this branch will fail until the next second branch succeeds.
The second branch _(?=_{n-1}) is devoted to the first underscore. It checks using a lookahead assertion the number of following underscores.
Related
I have two functions in PHP, trimmer($string,$number) and toUrl($string). I want to trim the urls extracted with toUrl(), to 20 characters for example. from https://www.youtube.com/watch?v=HU3GZTNIZ6M to https://www.youtube.com/wa...
function trimmer($string,$number) {
$string = substr ($string, 0, $number);
return $string."...";
}
function toUrl($string) {
$regex="/[^\W ]+[^\s]+[.]+[^\" ]+[^\W ]+/i";
$string= preg_replace($regex, "<a href='\\0'>".trimmer("\\0",20)."</a>",$string);
return $string;
}
But the problem is that the value of the match return \\0 not a variable like $url which could be easily trimmed with the function trimmer().
The Question is how do I apply substr() to \\0 something like this substr("\\0",0,20)?
What you want is preg_replace_callback:
function _toUrl_callback($m) {
return "" . trimmer($m[0], 20) ."";
}
function toUrl($string) {
$regex = "/[^\W ]+[^\s]+[.]+[^\" ]+[^\W ]+/i";
$string = preg_replace_callback($regex, "_toUrl_callback", $string);
return $string;
}
Also note that (side notes wrt your question):
You have a syntax error, '$regex' is not going to work (they don't replace var names in single-quoted strings)
You may want to look for better regexps to match URLs, you'll find plenty of them with a quick search
You may want to run through htmlspecialchars() your matches (mainly problems with "&", but that depends how you escape the rest of the string.
EDIT: Made it more PHP 4 friendly, requested by the asker.
As much as I've tried I can't seem to find the correct regex to locate what I'm after here.
I only want to select the first instance of the url that matches the domain www.myweb.com from the following...
Some text https://www.myweb.com/page/cat/323123442321-rghe432 and then another https://www.adifferentsite.com/fsdhjss/erwr
I need to completely ignore the second url www.adifferentsite.com and only work with the first one that matches www.myweb.com, ignoring any other possible instances of www.myweb.com
Once the first matching domain is discovered I need to store the rest of the url that comes after it...
page/cat/323123442321-rghe432
...into a new variable $newvar, so...
$newvar = 'page/cat/323123442321-rghe432';
I'm trying :
return preg_replace_callback( '/http://www.myweb.com/\/[0-9a-zA-Z]+/', array( __CLASS__, 'my_callback' ), $newvar );
I've read tons of documents on how to detect url's but can't find anything about detecting a specific url.
I really can't grasp how to formulate regex so this formula is incorrect. Any help would be greatly appreciated.
EDIT Edited the question to be a bit more specific and hopefully a bit easier to resolve.
You can use a preg_replace_callback and pass an array into the anonymous function (or just your custom callback function) to fill it with all the necessary URL parts.
Here is a demo:
$rests = array();
$re = '~\b(https?://)www\.myweb\.com/(\S+)~';
$str = "Some text https://www.myweb.com/page/cat/323123442321-rghe432 and then another https://www.adifferentsite.com/fsdhjss/erwr";
echo $result = preg_replace_callback($re, function ($m) use (&$rests) {
array_push($rests, $m[2]);
return $m[1] . "embed.myweb.com/" . $m[2];
}, $str) . PHP_EOL;
print_r($rests);
Results:
Some text https://embed.myweb.com/page/cat/323123442321-rghe432 and then another https://www.adifferentsite.com/fsdhjss/erwr
Array
(
[0] => page/cat/323123442321-rghe432
)
A couple of words:
'~\b(https?://)www\.myweb\.com/(\S+)~' has ~ as a regex delimiter, so you do not have to escape /
It is declared with a single-quoted literal, so you do not have to use double-escaping for \\S
It matches and captures into capturing groups 2 substrings: \b(https?://) (that matches a whole word http or https followed by ://) and (\S+) (that matches 1 or more non-whitespace characters). These capturing groups are marked with (...) in the pattern and can be accessed via $matches[n] where n is the id of the capturing group.
UPDATE
If you only need to replace the first occurrence of the URL, pass the limit argument to the preg_replace_callback:
$rest = "";
$re = '~\b(https?://)www\.myweb\.com/(\S+\b)~';
$str = "Some text https://www.myweb.com/page/cat/323123442321-rghe432, another http://www.myweb.com/page/cat/323123442321-rghe432 and then another https://www.adifferentsite.com/fsdhjss/erwr";
echo $result = preg_replace_callback($re, function ($m) use (&$rest) {
$rest = $m[2];
return $m[1] . "embed.myweb.com/" . $m[2];
}, $str, 1) . PHP_EOL;
//-LIMIT ^ - HERE -
echo $rest;
See another IDEONE demo
I have a group of letters, for example :
$word='estroaroint';
that can be arranged to be words like :
- store
- train
- restoration
- ...etc
They can be found in my file list 'dictionary.txt'.
A letter only can only be used once.
How to write a php script able to perform that?
I would try to manage it with this function: strpbrk() http://php.net/manual/en/function.strpbrk.php
It isn't really possible to do that in one step with a regex. However, it is possible to do it in two steps:
the first step find all the words in the dictionary that only contains the letters.
the second step filter words where letters are repeated.
Example (only for ascii range):
$pattern = '~\b[' . $word . ']{1,' . strlen($word) . '}+\b~';
if (preg_match_all($pattern, $dictionary, $m)) {
$chars = count_chars ($word, 1);
$result = array_filter($m[0], function ($i) use ($chars) {
foreach (count_chars($i, 1) as $k=>$v) {
if ($v > $chars[$k]) return false;
}
return true;
});
print_r($result);
}
PHP links: array_filter - count_chars
Note: to extend this script to multibyte characters, you need to write your own function mb_count_chars (since this function doesn't exist) that splits a multibyte string (you can use for example mb_substr, mb_strlen and a loop, or preg_split with ~(?=.)~u and the PREG_SPLIT_NO_EMPTY option). You need to add the u modifier to the regex pattern too and to change strlen to its multybyte equivalent.
assuming i have these texts 'x34' , '150px' , '650dpi' , 'e3r4t5' ... how can i get only numbers ? i mean i want 34 , 150 , 650 , 345 without any other character . i mean get the numbers this string has into one variable .
$str = "e3r4t5";
$str_numbers_only = preg_replace("/[^\d]/", "", $str);
// $number = (int) $str;
Sorry for joining the bandwagon late, rather than using Regex, I would suggest you use PHP's built in functions, which may be faster than Regex.
filter_var
flags for the filters
e.g. to get just numbers from the given string
<?php
$a = '!a-b.c3#j+dk9.0$3e8`~]\]2';
$number = str_replace(['+', '-'], '', filter_var($a, FILTER_SANITIZE_NUMBER_INT));
// Output is 390382
?>
To adhere to more strict standards for your question, I have updated my answer to give a better result.
I have added str_replace, as FILTER_SANITIZE_NUMBER_FLOAT or INT flag will not strip + and - chars from the string, because they are part of PHP's exception rule.
Though it has made the filter bit long, but it's now has less chance of failing or giving you unexpected results, and this will be faster than REGEX.
Edit:
1: Realized that with FILTER_SANITIZE_NUMBER_FLOAT, PHP won't strip these characters optionally .,eE, hence to get just pure numbers kindly use FILTER_SANITIZE_NUMBER_INT
2: If you have a PHP version less than 5.4, then kindly use array('+', '-') instead of the short array syntax ['+', '-'].
You can use a regular expression to remove any character that is not a digit:
preg_replace('/\D/', '', $str)
Here the pattern \D describes any character that is not a digit (complement to \d).
Use PHP FILTER functions if you are using PHP 5.2.X, 5.3.x,5.4 . Its highly recommended
$mixed_input = "e3r4t5";
$only_numbers = filter_var($mixed_input, FILTER_SANITIZE_NUMBER_INT);
Please Go through with this link to know more
Replace everything that isn't a number and use that value.
$str = "foo1bar2baz3";
$num = intval(preg_replace("/[^0-9]/", "", $str));
You could use the following function:
function extract_numbers($string) {
preg_match_all('/([\d]+)/', $string, $match);
return $match;
}
I am trying to remove the word "John" a certain number of times from a string. I read on the php manual that str_replace excepts a 4th parameter called "count". So I figured that can be used to specify how many instances of the search should be removed. But that doesn't seem to be the case since the following:
$string = 'Hello John, how are you John. John are you happy with your life John?';
$numberOfInstances = 2;
echo str_replace('John', 'dude', $string, $numberOfInstances);
replaces all instances of the word "John" with "dude" instead of doing it just twice and leaving the other two Johns alone.
For my purposes it doesn't matter which order the replacement happens in, for example the first 2 instances can be replaced, or the last two or a combination, the order of the replacement doesn't matter.
So is there a way to use str_replace() in this way or is there another built in (non-regex) function that can achieve what I'm looking for?
As Artelius explains, the last parameter to str_replace() is set by the function. There's no parameter that allows you to limit the number of replacements.
Only preg_replace() features such a parameter:
echo preg_replace('/John/', 'dude', $string, $numberOfInstances);
That is as simple as it gets, and I suggest using it because its performance hit is way too tiny compared to the tedium of the following non-regex solution:
$len = strlen('John');
while ($numberOfInstances-- > 0 && ($pos = strpos($string, 'John')) !== false)
$string = substr_replace($string, 'dude', $pos, $len);
echo $string;
You can choose either solution though, both work as you intend.
You've misunderstood the wording of the manual.
If passed, this will be set to the number of replacements performed.
The parameter is passed by reference and its value is changed by the function to indicate how many times the string was found and replaced. Its initial value is discarded.
There are a few things you could do to achieve this, but I can't think of one specific php function that will easily let you do this.
One option is to create your own replace function and utilize strripos and substr to do the replaces.
Another thing you can do is use preg_replace_callback and count the number of replacements you have done in the callback.
There's probably more ways but that's all I can think of on the fly. If performance is an issue I suggest you give both a try and do some simple benchmarks.
The cleanest, most-direct, single function call is to use preg_replace(). Its replacement limiting parameter makes the task intuitive and readable.
$string = preg_replace('/John/', 'dude', $string, $numberOfInstances);
The function is also attractive because making the search case-insensitive is as simple as adding the i pattern modifier to the end of the pattern. I won't delve into the usefulness of word boundaries (\b).
If a search string might contain characters with special meaning to the regex engine, then preg_quote() will be necessary -- this diminishes the beauty of the technique but not prohibitively so.
$search = '$5.99';
$pattern = '/' . preg_quote($search, '/') . '/';
$string = preg_replace($pattern, 'free', $string, $numberOfInstances);
For anyone who has an unnatural bias against regex functions, this can be done without regex and without looping -- it will be case-sensitive though.
Limited Explode & Implode: (Demo)
$numberOfInstances = 2;
$string = 'Hello John, how are you John. John are you happy with your life John?';
// explode here -^^^^ and ---------^^^^ only to create the following array:
// 0 => 'Hello ',
// 1 => ', how are you ',
// 2 => '. John are you happy with your life John?'
echo implode('dude', explode('John', $string, $numberOfInstances + 1));
Output:
Hello dude, how are you dude. John are you happy with your life John?
Notice the explode's limiting parameter dictates how many elements are generated, not how many explosions are executed on the string.
function str_replace_occurrences($find, $replace, $string, $count = -1) {
// current occrurence
$current = 0;
// while any occurrence
while (($pos = strpos($string, $find)) != false) {
// update length of str (size of string is changing)
$len = strlen($find);
// found next one
$current++;
// check if we've reached our target
// -1 is used to replace all occurrence
if($current <= $count || $count == -1) {
// do replacement
$string = substr_replace($string, $replace, $pos, $len);
} else {
// we've reached our
break;
}
}
return $string;
}
Artelius has already described how the function works, ill just show you how to do this via the manual methods:
function str_replace_occurrences($find,$replace,$string,$count = 0)
{
if($count == 0)
{
return str_replace($find,$replace,$string);
}
$pos = 0;
$len = strlen($find);
while($pos < $count && false !== ($pos = strpos($string,$find,$pos)))
{
$string = substr_replace($string,$replace,$pos,$len);
}
return $string;
}
This is untested but should work.