This question already has answers here:
My regex is matching too much. How do I make it stop? [duplicate]
(5 answers)
Closed 2 years ago.
I have to modify an URL like this:
$string = "/st:1/sc:RsrlYQhSQvs=/fp:1/g:3/start:2015-07-01/end:2015-07-30";
Namely, I want to delete st:1 with a regex. I used:
preg_replace("/\/st:(.*)\//",'',$string)
but I got
end:2015-07-30
while I would like to get:
/sc:RsrlYQhSQvs=/fp:1/g:3/start:2015-07-01/end:2015-07-30
Same if I would like to delete fp:1.
You can use:
$string = preg_replace('~/st:[^/]*~','',$string);
[^/]* will only match till next /
You are using greedy matching with . that matches any character.
Use a more restricted pattern:
preg_replace("/\/st:[^\/]*/",'',$string)
The [^\/]* negated character class only matches 0 or more characters other than /.
Another solution would be to use lazy matching with *? quantifier, but it is not that efficient as with the negated character class.
FULL REGEX EXPLANATION:
\/st: - literal /st:
[^\/]* - 0 or more characters other than /.
You need to add ? in your regex:-
<?php
$string = "/st:1/sc:RsrlYQhSQvs=/fp:1/g:3/start:2015-07-01/end:2015-07-30";
echo preg_replace("/\/st:(.*?)\//",'',$string)
?>
Output:- https://eval.in/397658
Based on this same you can do for next things also.
Instead of using regex here you should make parsing utility functions for your special format string, they are simple, they don't take to long to write and they will make your life a lot easier:
function readPath($path) {
$parameters = array();
foreach(explode('/', $path) as $piece) {
// Here we make sure we have something
if ($piece == "") {
continue;
}
// This here is just a fancy way of splitting the array returned
// into two variables.
list($key, $value) = explode(':', $piece);
$parameters[$key] = $value;
}
return $parameters;
}
function writePath($parameters) {
$path = "";
foreach($parameters as $key => $value) {
$path .= "/" . implode(":", array($key, $value));
}
return $path;
}
Now you can just work on it as a php array, in this case you would go:
$parameters = readPath($string);
unset($parameters['st']);
$string = writePath($parameters);
This makes for much more readable and reusable code, additionally since most of the time you are dealing with only slight variations of this format you can just change the delimiters each time or even abstract these functions to using different delimiters.
Another way to deal with this is to convert the string to conform to a normal path query, using something like:
function readPath($path) {
return parse_str(strtr($path, "/:", "&="));
}
In your case though since you are using the "=" character in a url you would also need to url encode each value so as to not conflict with the format, this would involve similarly structured code to above though.
Related
I am getting an "Array to string conversion error on PHP";
I am using the "variable" (that should be a string) as the third parameter to str_replace. So in summary (very simplified version of whats going on):
$str = "very long string";
str_replace("tag", $some_other_array, $str);
$str is throwing the error, and I have been trying to fix it all day, the thing I have tried is:
if(is_array($str)) die("its somehow an array");
serialize($str); //inserted this before str_replace call.
I have spent all day on it, and no its not something stupid like variables around the wrong way - it is something bizarre. I have even dumped it to a file and its a string.
My hypothesis:
The string is too long and php can't deal with it, turns into an array.
The $str value in this case is nested and called recursively, the general flow could be explained like this:
--code
//pass by reference
function the_function ($something, &$OFFENDING_VAR, $something_else) {
while(preg_match($something, $OFFENDING_VAR)) {
$OFFENDING_VAR = str_replace($x, y, $OFFENDING_VAR); // this is the error
}
}
So it may be something strange due to str_replace, but that would mean that at some point str_replace would have to return an array.
Please help me work this out, its very confusing and I have wasted a day on it.
---- ORIGINAL FUNCTION CODE -----
//This function gets called with multiple different "Target Variables" Target is the subject
//line, from and body of the email filled with << tags >> so the str_replace function knows
//where to replace them
function perform_replacements($replacements, &$target, $clean = TRUE,
$start_tag = '<<', $end_tag = '>>', $max_substitutions = 5) {
# Construct separate tag and replacement value arrays for use in the substitution loop.
$tags = array();
$replacement_values = array();
foreach ($replacements as $tag_text => $replacement_value) {
$tags[] = $start_tag . $tag_text . $end_tag;
$replacement_values[] = $replacement_value;
}
# TODO: this badly needs refactoring
# TODO: auto upgrade <<foo>> to <<foo_html>> if foo_html exists and acting on html template
# Construct a regular expression for use in scanning for tags.
$tag_match = '/' . preg_quote($start_tag) . '\w+' . preg_quote($end_tag) . '/';
# Perform the substitution until all valid tags are replaced, or the maximum substitutions
# limit is reached.
$substitution_count = 0;
while (preg_match ($tag_match, $target) && ($substitution_count++ < $max_substitutions)) {
$target = serialize($target);
$temp = str_replace($tags,
$replacement_values,
$target); //This is the line that is failing.
unset($target);
$target = $temp;
}
if ($clean) {
# Clean up any unused search values.
$target = preg_replace($tag_match, '', $target);
}
}
How do you know $str is the problem and not $some_other_array?
From the manual:
If search and replace are arrays, then str_replace() takes a value
from each array and uses them to search and replace on subject. If
replace has fewer values than search, then an empty string is used for
the rest of replacement values. If search is an array and replace is a
string, then this replacement string is used for every value of
search. The converse would not make sense, though.
The second parameter can only be an array if the first one is as well.
I am trying to use a License PHP System…
I will like to show the status of their license to the users.
The license Server gives me this:
name=Service_Name;nextduedate=2013-02-25;status=Active
I need to have separated the data like this:
$name = “Service_Name”;
$nextduedate = “2013-02-25”;
$status = “Active”;
I have 2 days tring to resolve this problem with preg_match_all but i cant :(
This is basically a query string if you replace ; with &. You can try parse_str() like this:
$string = 'name=Service_Name;nextduedate=2013-02-25;status=Active';
parse_str(str_replace(';', '&', $string));
echo $name; // Service_Name
echo $nextduedate; // 2013-02-25
echo $status; // Active
This can rather simply be solved without regex. The use of explode() will help you.
$str = "name=Service_Name;nextduedate=2013-02-25;status=Active";
$split = explode(";", $str);
$structure = array();
foreach ($split as $element) {
$element = explode("=", $element);
$$element[0] = $element[1];
}
var_dump($name);
Though I urge you to use an array instead. Far more readable than inventing variables that didn't exist and are not explicitly declared.
It sounds like you just want to break the text down into separate lines along the semicolons, add a dollar sign at the front and then add spaces and quotes. I'm not sure you can do that in one step with a regular expression (or at least I don't want to think about what that regular expression would look like), but you can do it over multiple steps.
Use preg_split() to split the string into an array along the
semicolons.
Loop over the array.
Use str_replace to replace each '=' with ' = "'.
Use string concatenation to add a $ to the front and a "; to the end of each string.
That should work, assuming your data doesn't include quotes, equal signs, semicolons, etc. within the data. If it does, you'll have to figure out the parsing rules for that.
I'm using mb_strtolower to make a string lowercase, but sometimes text contains urls with upper case. And when I use mb_strtolower, of course the urls changing and not working.
How can I convert string to lower without changin urls?
Since you have not posted your string, this can be only generally answered.
Whenever you use a function on a string to make it lower-case, the whole string will be made lower-case. String functions are aware of strings only, they are not aware of the contents written within these strings specifically.
In your scenario you do not want to lowercase the whole string I assume. You want to lowercase only parts of that string, other parts, the URLs, should not be changed in their case.
To do so, you must first parse your string into these two different parts, let's call them text and URLs. Then you need to apply the lowercase function only on the parts of type text. After that you need to combine all parts together again in their original order.
If the content of the string is semantically simple, you can split the string at spaces. Then you can check each part, if it begins with http:// or https:// (is_url()?) and if not, perform the lowercase operation:
$text = 'your content http://link.me/now! might differ';
$fragments = explode(' ', $text);
foreach($fragments as &$fragment) {
if (is_not_url($fragment))
$fragment = strtolower($fragment) // or mb_strtolower
;
}
unset($fragment); // remove reference
$lowercase = implode(' ', $fragments);
To have this code to work, you need to define the is_not_url() function. Additionally, the original text must contain contents that allows to work on rudimentary parsing it based on the space separator.
Hopefully this example help you getting along with coding and understanding your problem.
Here you go, iterative, but as fine as possible.
function strtolower_sensitive ( $input ) {
$regexp = "#((http|https|ftp)://(\S*?\.\S*?))(\s|\;|\)|\]|\[|\{|\}|,|\"|'|:|\<|$|\.\s)#ie";
if(preg_match_all($regexp, $input, $matches, PREG_SET_ORDER)) {
for( $i=0, $hist=array(); $i<=count($matches); ++$i ) {
str_replace( $u=$matches[$i][0], $n="sxxx".$i+1, $input ); $hist[]=array($u,$n);
}
$input = strtolower($input);
foreach ( $hist as $h ) {
str_replace ( $h[1], $h[0], $input );
}
}
return $input;
}
$input is your string, $output will be your answer. =)
I am trying to validate a Youtube URL using regex:
preg_match('~http://youtube.com/watch\?v=[a-zA-Z0-9-]+~', $videoLink)
It kind of works, but it can match URL's that are malformed. For example, this will match ok:
http://www.youtube.com/watch?v=Zu4WXiPRek
But so will this:
http://www.youtube.com/watch?v=Zu4WX£&P!ek
And this wont:
http://www.youtube.com/watch?v=!Zu4WX£&P4ek
I think it's because of the + operator. It's matching what seems to be the first character after v=, when it needs to try and match everything behind v= with [a-zA-Z0-9-]. Any help is appreciated, thanks.
To provide an alternative that is larger and much less elegant than a regex, but works with PHP's native URL parsing functions so it might be a bit more reliable in the long run:
$url = "http://www.youtube.com/watch?v=Zu4WXiPRek";
$query_string = parse_url($url, PHP_URL_QUERY); // v=Zu4WXiPRek
$query_string_parsed = array();
parse_str($query_string, $query_string_parsed); // an array with all GET params
echo($query_string_parsed["v"]); // Will output Zu4WXiPRek that you can then
// validate for [a-zA-Z0-9] using a regex
The problem is that you are not requiring any particular number of characters in the v= part of the URL. So, for instance, checking
http://www.youtube.com/watch?v=Zu4WX£&P!ek
will match
http://www.youtube.com/watch?v=Zu4WX
and therefore return true. You need to either specify the number of characters you need in the v= part:
preg_match('~http://youtube.com/watch\?v=[a-zA-Z0-9-]{10}~', $videoLink)
or specify that the group [a-zA-Z0-9-] must be the last part of the string:
preg_match('~http://youtube.com/watch\?v=[a-zA-Z0-9-]+$~', $videoLink)
Your other example
http://www.youtube.com/watch?v=!Zu4WX£&P4ek
does not match, because the + sign requires that at least one character must match [a-zA-Z0-9-].
Short answer:
preg_match('%(http://www.youtube.com/watch\?v=(?:[a-zA-Z0-9-])+)(?:[&"\'\s])%', $videoLink)
There are a few assumptions made here, so let me explain:
I added a capturing group ( ... ) around the entire http://www.youtube.com/watch?v=blah part of the link, so that we can say "I want get the whole validated link up to and including the ?v=movieHash"
I added the non-capturing group (?: ... ) around your character set [a-zA-Z0-9-] and left the + sign outside of that. This will allow us to match all allowable characters up to a certain point.
Most importantly, you need to tell it how you expect your link to terminate. I'm taking a guess for you with (?:[&"\'\s])
?) Will it be in html format (e.g. anchor tag) ? If so, the link in href will obviously end with a " or '.
?) Or maybe there's more to the query string, so there would be an & after the value of v.
?) Maybe there's a space or line break after the end of the link \s.
The important piece is that you can get much more accurate results if you know what's surrounding what you are searching for, as is the case with many regular expressions.
This non-capturing group (in which I'm making assumptions for you) will take a stab at finding and ignoring all the extra junk after what you care about (the ?v=awesomeMovieHash).
Results:
http://www.youtube.com/watch?v=Zu4WXiPRek
- Group 1 contains the http://www.youtube.com/watch?v=Zu4WXiPRek
http://www.youtube.com/watch?v=Zu4WX&a=b
- Group 1 contains http://www.youtube.com/watch?v=Zu4WX
http://www.youtube.com/watch?v=!Zu4WX£&P4ek
- No match
a href="http://www.youtube.com/watch?v=Zu4WX&size=large"
- Group 1 contains http://www.youtube.com/watch?v=Zu4WX
http://www.youtube.com/watch?v=Zu4WX£&P!ek
- No match
The "v=..." blob is not guaranteed to be the first parameter in the query part of the URL. I'd recommend using PHP's parse_url() function to break the URL into its component parts. You can also reassemble a pristine URL if someone began the string with "https://" or simply used "youtube.com" instead of "www.youtube.com", etc.
function get_youtube_vidid ($url) {
$vidid = false;
$valid_schemes = array ('http', 'https');
$valid_hosts = array ('www.youtube.com', 'youtube.com');
$valid_paths = array ('/watch');
$bits = parse_url ($url);
if (! is_array ($bits)) {
return false;
}
if (! (array_key_exists ('scheme', $bits)
and array_key_exists ('host', $bits)
and array_key_exists ('path', $bits)
and array_key_exists ('query', $bits))) {
return false;
}
if (! in_array ($bits['scheme'], $valid_schemes)) {
return false;
}
if (! in_array ($bits['host'], $valid_hosts)) {
return false;
}
if (! in_array ($bits['path'], $valid_paths)) {
return false;
}
$querypairs = explode ('&', $bits['query']);
if (count ($querypairs) < 1) {
return false;
}
foreach ($querypairs as $querypair) {
list ($key, $value) = explode ('=', $querypair);
if ($key == 'v') {
if (preg_match ('/^[a-zA-Z0-9\-_]+$/', $value)) {
# Set the return value
$vidid = $value;
}
}
}
return $vidid;
}
Following regex will match any youtube link:
$pattern='#(((http(s)?://(www\.)?)|(www\.)|\s)(youtu\.be|youtube\.com)/(embed/|v/|watch(\?v=|\?.+&v=|/))?([a-zA-Z0-9._\/~#&=;%+?-\!]+))#si';
$var="UseCountry=1
UseCountryDefault=1
UseState=1
UseStateDefault=1
UseLocality=1
UseLocalityDefault=1
cantidad_productos=5
expireDays=5
apikey=ABQIAAAAFHktBEXrHnX108wOdzd3aBTupK1kJuoJNBHuh0laPBvYXhjzZxR0qkeXcGC_0Dxf4UMhkR7ZNb04dQ
distancia=15
AutoCoord=1
user_add_locality=0
SaveContactForm=0
ShowVoteRating=0
Listlayout=0
WidthThumbs=100
HeightThumbs=75
WidthImage=640
HeightImage=480
ShowImagesSystem=1
ShowOrderBy=0
ShowOrderByDefault=0
ShowOrderDefault=DESC
SimbolPrice=$
PositionPrice=0
FormatPrice=0
ShowLogoAgent=1
ShowReferenceInList=1
ShowCategoryInList=1
ShowTypeInList=1
ShowAddressInList=1
ShowContactLink=1
ShowMapLink=1
ShowAddShortListLink=1
ShowViewPropertiesAgentLink=1
ThumbsInAccordion=5
WidthThumbsAccordion=100
HeightThumbsAccordion=75
ShowFeaturesInList=1
ShowAllParentCategory=0
AmountPanel=
AmountForRegistered=5
RegisteredAutoPublish=1
AmountForAuthor=5
AmountForEditor=5
AmountForPublisher=5
AmountForManager=5
AmountForAdministrator=5
AutoPublish=1
MailAdminPublish=1
DetailLayout=0
ActivarTabs=0
ActivarDescripcion=1
ActivarDetails=1
ActivarVideo=1
ActivarPanoramica=1
ActivarContactar=1
ContactMailFormat=1
ActivarReservas=1
ActivarMapa=1
ShowImagesSystemDetail=1
WidthThumbsDetail=120
HeightThumbsDetail=90
idCountryDefault=1
idStateDefault=1
ms_country=1
ms_state=1
ms_locality=1
ms_category=1
ms_Subcategory=1
ms_type=1
ms_price=1
ms_bedrooms=1
ms_bathrooms=1
ms_parking=1
ShowTextSearch=1
minprice=
maxprice=
ms_catradius=1
idcatradius1=
idcatradius2=
ShowTotalResult=1
md_country=1
md_state=1
md_locality=1
md_category=1
md_type=1
showComments=0
useComment2=0
useComment3=0
useComment4=0
useComment5=0
AmountMonthsCalendar=3
StartYearCalendar=2009
StartMonthCalendar=1
PeriodOnlyWeeks=0
PeriodAmount=3
PeriodStartDay=1
apikey=ABQIAAAAJ879Hg7OSEKVrRKc2YHjixSmyv5A3ewe40XW2YiIN-ybtu7KLRQiVUIEW3WsL8vOtIeTFIVUXDOAcQ
";
in that string only i want "api==ABQIAAAAJ879Hg7OSEKVrRKc2YHjixSmyv5A3ewe40XW2YiIN-ybtu7KLRQiVUIEW3WsL8vOtIeTFIVUXDOAcQ";
plz guide me correctly;
EDIT
As shamittomar pointed out, the parse_str will not work for this situation, posted the proper regex below.
Given this seems to be a QUERY STRING, use the parse_str() function PHP provides.
UPDATE
If you want to do it with regex using preg_match() as powertieke pointed out:
preg_match('/apikey=(.*)/', $var, $matches);
echo $matches[1];
Should do the trick.
preg_match(); should be right up your alley
people are so fast to jump to preg match when this can be done with regular string functions thats faster.
$string = '
expireDays=5
apikey=ABQIAAAAFHktBEXrHnX108wOdzd3aBTupK1kJuoJNBHuh0laPBvYXhjzZxR0qkeXcGC_0Dxf4UMhkR7ZNb04dQ
distancia=15
AutoCoord=1';
//test to see what type of line break it is and explode by that.
$parts = (strstr($string,"\r\n") ? explode("\r\n",$string) : explode("\n",$string));
$data = array();
foreach($parts as $part)
{
$sub = explode("=",trim($part));
if(!empty($sub[0]) || !empty($sub[1]))
{
$data[$sub[0]] = $sub[1];
}
}
and use $data['apikey'] for your api key, i would also advise you to wrpa in function.
I can bet this is a better way to parse the string and much faster.
function ParsemyString($string)
{
$parts = (strstr($string,"\r\n") ? explode("\r\n",$string) : explode("\n",$string));
$data = array();
foreach($parts as $part)
{
$sub = explode("=",trim($part));
if(!empty($sub[0]) || !empty($sub[1]))
{
$data[$sub[0]] = $sub[1];
}
}
return $data;
}
$data = ParsemyString($string);
First of all, you are not looking for
api==ABQIAAAAJ879Hg7OSEKVrRKc2YHjixSmyv5A3ewe40XW2YiIN-ybtu7KLRQiVUIEW3WsL8vOtIeTFIVUXDOAcQ
but you are looking for
apikey=ABQIAAAAJ879Hg7OSEKVrRKc2YHjixSmyv5A3ewe40XW2YiIN-ybtu7KLRQiVUIEW3WsL8vOtIeTFIVUXDOAcQ
It is important to know if the api-key property always occurs at the end and if the length of the api-key value is always the same. I this is the case you could use the PHP substr() function which would be easiest.
If not you would most probably need a regular expression which you can feed to PHPs preg_match() function. Something along the lines of apikey==[a-zA-Z0-9\-] Which matches an api-key containing a-z in both lowercase and uppercase and also allows for dashes in the key. If you are using the preg_match() function you can retrieve the matches (and thus your api-key value).