I have a string, something like
bbbbabbbbbccccc
Are there any way for me to replace all the letters "b" after the only one letter "a" into "c" without having to split the string, using PHP?
bbbbacccccccccc
odd question.
echo preg_replace('/a(.*)$/e', "'a'.strtr($1, 'b', 'c')", 'bbbabbbbbccccc');
preg_replace matches everything to the right of 'a' with regex. the e modifier in the regex evaluates the replacement string as code. the code in the replacement string uses strtr() to replace 'b's with 'c's.
Here are three options.
First, a split. Yes, I know you want to do it without a split.
$string = 'bbbbabbbbbccccc';
$parts = preg_split('/(a)/', $string, 2, PREG_SPLIT_DELIM_CAPTURE);
// Parts now looks like:
// array('bbb', 'a', 'bbbbcccc');
$parts[2] = str_replace('b', 'c', $parts[2]);
$correct_string = join('', $parts);
Second, a position search and a substring replacement.
$string = 'bbbbabbbbbccccc';
$first_a_index = strpos($string, 'a');
if($first_a_index !== false) {
// Now, grab everything from that first 'a' to the end of the string.
$replaceable = substr($string, $first_a_index);
// Replace it.
$replaced = str_replace('b', 'c', $replaceable );
// Now splice it back in
$string = substr_replace($string, $replaced, $first_a_index);
}
Third, I was going to post a regex, but the one dqhendricks posted is just as good.
These code examples are verbose for clarity, and can be reduced to one-or-two-liners.
$s = 'bbbbabbbbbccccc';
echo preg_replace('/((?:(?!\A)\G|(?<!a)a(?!a))[^b]*)b/', '$1c', $s);
\G matches the position where the previous match ended. On the first match attempt, \G matches the beginning of the string like \A. We don't want that, so we use (?!\A) to prevent it.
(?<!a)a(?!a) matches an a that's neither preceded nor followed by an a. The a is captured in group #1 so we can plug it back into the replacement with $1.
This is a "pure regex" solution, meaning it does the whole job in one call to preg_replace and doesn't rely on embedded code and the /e modifier. It's good to know in case you ever find yourself working within those constraints, but it definitely shouldn't be your first resort.
Related
For example, if I want to get rid of the repeating numeric suffix from the end of an expression like this:
some_text_here_1
Or like this:
some_text_here_1_5
and I want finally receive something like this:
some_text_here
What's the best and flexible solution?
$newString = preg_replace("/_?\d+$/","",$oldString);
It is using regex to match an optional underscore (_?) followed by one or more digits (\d+), but only if they are the last characters in the string ($) and replacing them with the empty string.
To capture unlimited _ numbers, just wrap the whole regex (except the $) in a capture group and put a + after it:
$newString = preg_replace("/(_?\d+)+$/","",$oldString);
If you only want to remove a numberic suffix if it is after an underscore (e.g. you want some_text_here14 to not be changed, but some_text_here_14 to be changed), then it should be:
$newString = preg_replace("/(_\d+)+$/","",$oldString);
Updated to fix more than one suffix
Strrpos is far better than regex on such a simple string problem.
$str = "some_text_here_13_15";
While(is_numeric(substr($str, strrpos($str, "_")+1))){
$str = substr($str,0 , strrpos($str, "_"));
}
Echo $str;
Strrpos finds the last "_" in str and if it's numeric remove it.
https://3v4l.org/OTdb9
Just to give you an idea of what I mean with regex not being a good solution on this here is the performance.
Regex:
https://3v4l.org/Tu8o2/perf#output
0.027 seconds for 100 runs.
My code with added numeric check:
https://3v4l.org/dkAqA/perf#output
0.003 seconds for 100 runs.
This new code performs even better than before oddly enough, regex is very slow. Trust me on that
You be the judge on what is best.
First you'll want to do a preg_replace() in order to remove all digits by using the regex /\d+/. Then you'll also want to trim any underscores from the right using rtrim(), providing _ as the second parameter.
I've combined the two in the following example:
$string = "some_text_here_1";
echo rtrim(preg_replace('/\d+/', '', $string), '_'); // some_text_here
I've also created an example of this at 3v4l here.
Hope this helps! :)
$reg = '#_\d+$#';
$replace = '';
echo preg_replace($reg, $replace, $string);
This would do
abc_def_ghi_123 > abc_def_ghi
abc_def_1 > abc_def
abc_def_ghi > abc_def_ghi
abd_def_ > abc_def_
abc_123_def > abd_123_def
in case of abd_def_123_345 > abc_def
one could change the line
$reg = '#(?:_\d+)+$#';
I have some string, for example:
cats, e.g. Barsik, are funny. And it is true. So,
And I want to get as result:
cats, e.g. Barsik, are funny.
My try:
mb_ereg_search_init($text, '((?!e\.g\.).)*\.[^\.]');
$match = mb_ereg_search_pos();
But it gets position of second dot (after word "true").
How to get desired result?
Since a naive approach works for you, I am posting an answer. However, please note that detecting a sentence end is a very difficult task for a regex, and although it is possible to some degree, an NLP package should be used for that.
Having said that, I suggested using
'~(?<!\be\.g)\.(?=\s+\p{Lu})~ui'
The regex matches any dot (\.) that is not preceded with a whole word e.g (see the negative lookbehind (?<!\be\.g)), but that is followed with 1 or more whitespaces (\s+) followed with 1 uppercase Unicode letter \p{Lu}.
See the regex demo
The case insensitive i modifier does not impact what \p{Lu} matches.
The ~u modifier is required since you are working with Unicode texts (like Russian).
To get the index of the first occurrence, use a preg_match function with the PREG_OFFSET_CAPTURE flag. Here is a bit simplified regex you supplied in the comments:
preg_match('~(?<!т\.н)(?<!т\.к)(?<!e\.g)\.(?=\s+\p{L})~iu', $text, $match, PREG_OFFSET_CAPTURE);
See the lookaheads are executed one by one, and at the same location in string, thus, you do not have to additionally group them inside a positive lookahead. See the regex demo.
IDEONE demo:
$re = '~(?<!т\.н)(?<!т\.к)(?<!e\.g)\.(?=\s+\p{L})~iu';
$str = "cats, e.g. Barsik, are funny. And it is true. So,";
preg_match($re, $str, $match, PREG_OFFSET_CAPTURE);
echo $match[0][1];
Here are two approaches to get substring from start to second last . position of the initial string:
using strrpos and substr functions:
$str = 'cats, e.g. Barsik, and e.g. Lusya are funny. And it is true. So,';
$len = strlen($str);
$str = substr($str, 0, (strrpos($str, '.', strrpos($str, '.') - $len - 1) - $len) + 1);
print_r($str); // "cats, e.g. Barsik, and e.g. Lusya are funny."
using array_reverse, str_split and array_search functions:
$str = 'cats, e.g. Barsik, and e.g. Lusya are funny. And it is true. So,';
$parts = array_reverse(str_split($str));
$pos = array_search('.', $parts) + 1;
$str = implode("", array_reverse(array_slice($parts, array_search('.', array_slice($parts, $pos)) + $pos)));
print_r($str); // "cats, e.g. Barsik, and e.g. Lusya are funny."
Here is my concern,
I have a string and I need to extract chraracters two by two.
$str = "abcdef" should return array('ab', 'bc', 'cd', 'de', 'ef'). I want to use preg_match_all instead of loops. Here is the pattern I am using.
$str = "abcdef";
preg_match_all('/[\w]{2}/', $str);
The thing is, it returns Array('ab', 'cd', 'ef'). It misses 'bc' and 'de'.
I have the same problem if I want to extract a certain number of words
$str = "ab cd ef gh ij";
preg_match_all('/([\w]+ ){2}/', $str); // returns array('ab cd', 'ef gh'), I'm also missing the last part
What am I missing? Or is it simply not possible to do so with preg_match_all?
For the first problem, what you want to do is match overlapping string, and this requires zero-width (not consuming text) look-around to grab the character:
/(?=(\w{2}))/
The regex above will capture the match in the first capturing group.
DEMO
For the second problem, it seems that you also want overlapping string. Using the same trick:
/(?=(\b\w+ \w+\b))/
Note that \b is added to check the boundary of the word. Since the match does not consume text, the next match will be attempted at the next index (which is in the middle of the first word), instead of at the end of the 2nd word. We don't want to capture from middle of a word, so we need the boundary check.
Note that \b's definition is based on \w, so if you ever change the definition of a word, you need to emulate the word boundary with look-ahead and look-behind with the corresponding character set.
DEMO
In case if you need a Non-Regex solution, Try this...
<?php
$str = "abcdef";
$len = strlen($str);
$arr = array();
for($count = 0; $count < ($len - 1); $count++)
{
$arr[] = $str[$count].$str[$count+1];
}
print_r($arr);
?>
See Codepad.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Preg Replace - replace second occurance of a match
I have a string that includes the word rules twice. I need to find and replace the 2nd word. Tried fooling around with str_replace() but couldn't get anything, the 4th parameter wasn't what I expected.
Here is an example string:
http://localhost/proj1/modstart/admin/index.php?i=rules&sid=397ab1f6b8eb8a17787438a7e2e60ea3&mode=rules
After my replace it should look like this:
http://localhost/proj1/modstart/admin/index.php?i=rules&sid=397ab1f6b8eb8a17787438a7e2e60ea3&mode=manage
I read that preg_replace() could help, but I don't know how to write patterns.
Ideas?
P.S: Don't suggest splitting the string into two variables, that wouldn't serve my needs.
It would be a very good idea to learn about regular expressions. In PHP, you can accomplish your find/replace like this:
$result = preg_replace('/(rules.*?)rules/','$1manage',$str,1);
It basically finds "rules" once, then anything, then rules a second time, then puts it all back before the second match and replaces the word.
To not use a regular expression, and also to not store anything into a second variable, you could use str_replace() with a little magic from strpos():
$string = substr($string, 0, strpos($str, 'rules') + 5) . str_replace('rules', 'whatever', substr($string, strpos($string, 'rules') + 5));
This will take the full string up-to the end of the first instance of rules and then do the string-replacement on the second-part of the string which will contain any other instance of the word.
The same thing, but a little more cleaner (yes, by using a second variable):
$pos = strpos($string, 'rules') + 5;
$string = substr($string, 0, $pos) . str_replace('rules', 'whatever', substr($string, $pos));
If the word to find+replace is dynamic or you want to use a different word on different pages, you could make that a variable, like this:
$findMe = 'rules';
$replaceWith = 'whatever';
$pos = strpos($string, $findMe) + strlen($findMe);
$string = substr($string, 0, $pos) . str_replace($findMe, $replaceWith, substr($string, $pos));
You should use regex >>
$new = preg_replace('/\brules\b(?!.*\brules\b)/', 'manage', $old);
It is a good idea to use word boundaries \b, so it will not match some larger strings that contain "rules", such as "preudorules".
Negative lookahead (?!.*\brules\b) ensures there is no other word "rules" behind, so the one you are replacing is the last one.
I need to know how I can replace the last "s" from a string with ""
Let's say I have a string like testers and the output should be tester.
It should just replace the last "s" and not every "s" in a string
how can I do that in PHP?
if (substr($str, -1) == 's')
{
$str = substr($str, 0, -1);
}
Update: Ok it is also possible without regular expressions using strrpos ans substr_replace:
$str = "A sentence with 'Testers' in it";
echo substr_replace($str,'', strrpos($str, 's'), 1);
// Ouputs: A sentence with 'Tester' in it
strrpos returns the index of the last occurrence of a string and substr_replace replaces a string starting from a certain position.
(Which is the same as Gordon proposed as I just noticed.)
All answers so far remove the last character of a word. However if you really want to replace the last occurrence of a character, you can use preg_replace with a negative lookahead:
$s = "A sentence with 'Testers' in it";
echo preg_replace("%s(?!.*s.*)%", "", $string );
// Ouputs: A sentence with 'Tester' in it
$result = rtrim($str, 's');
$result = str_pad($result, strlen($str) - 1, 's');
See rtrim()
Your question is somewhat unclear whether you want to remove the s from the end of the string or the last occurence of s in the string. It's a difference. If you want the first, use the solution offered by zerkms.
This function removes the last occurence of $char from $string, regardless of it's position in the string or returns the whole string, when $char does not occur in the string.
function removeLastOccurenceOfChar($char, $string)
{
if( ($pos = strrpos($string, $char)) !== FALSE) {
return substr_replace($string, '', $pos, 1);
}
return $string;
}
echo removeLastOccurenceOfChar('s', "the world's greatest");
// gives "the world's greatet"
If your intention is to inflect, e.g singularize/pluralize words, then have a look at this simple inflector class to know which route to take.
$str = preg_replace("/s$/i","",rtrim($str));
The very simplest solution is using rtrim()
That is exactly what that function is intended to be used for:
Strip whitespace (or other characters) from the end of a string.
Nothing simpler than that, I am not sure why, and would not follow the suggestions in this thread going from regex to "if/else" blocks.
This is your code:
$string = "Testers";
$stripped = rtrim( $string, 's' );
The output will be:
Tester