Match everything in brackets after a specific character - php

I have the following string:
$text = 'These are my cards. They are {{Archetype|Agumon}} and {{Fire|Gabumon}}'
I'm trying to replace all instances of occurrences like {{Archetype|Agumon}} into [Agumon].
I've been struggling to get my head around it and have come up with this so far:
$string = preg_replace('#\{\{(.*?)\}\}#', '[$1]', $text);
This results in:
These are my cards. They are [Archetype|Agumon] and [Fire|Gabumon]
So I am currently matching the full text found in between the double curly brackets.
I thought it would be something like this: \|(.*?) to get the match after the | character in the curly brackets but to no avail.

You may use:
\{\{[^}]*\|([^}]*)\}\}
Demo.
Breakdown:
\{\{ - Match "{{" literally.
[^}]* - Greedily match zero or more characters other than '}'.
\| - Match a pipe character.
([^}]*) - Match zero or more characters other than '}' and capture them in group 1.
\}\} - Match "}}" literally.

Use
preg_replace('/{{(?:(?!{|}})[^|]*\|(.*?))}}/s', '[$1]', $text)
See proof. It will support { and } in the part before the pipe.
Explanation
--------------------------------------------------------------------------------
{{ '{{'
--------------------------------------------------------------------------------
(?: group, but do not capture:
--------------------------------------------------------------------------------
(?! look ahead to see if there is not:
--------------------------------------------------------------------------------
{ '{'
--------------------------------------------------------------------------------
| OR
--------------------------------------------------------------------------------
}} '}}'
--------------------------------------------------------------------------------
) end of look-ahead
--------------------------------------------------------------------------------
[^|]* any character except: '|' (0 or more
times (matching the most amount
possible))
--------------------------------------------------------------------------------
\| '|'
--------------------------------------------------------------------------------
( group and capture to \1:
--------------------------------------------------------------------------------
.*? any character (0 or more
times (matching the least amount
possible))
--------------------------------------------------------------------------------
) end of \1
--------------------------------------------------------------------------------
) end of grouping
--------------------------------------------------------------------------------
}} '}}'
PHP code:
$text = 'These are my cards. They are {{Archetype|Agumon}} and {{Fire|Gabumon}}';
echo preg_replace('/{{(?:(?!{|}})[^|]*\|(.*?))}}/s', '[$1]', $text);
Results: These are my cards. They are [Agumon] and [Gabumon]

Related

Regex to find and expression and insert into the middle

I'll be brief as I can. I'm trying to use preg_replace's regex to find a digit, but I want to non destructively edit the string.
an example: (albeit this is an approximation due to data protection)
$subject_string = 'section 1.1: Disability ........' ;
$outcome = preg_replace( '/$section[\d.\d]+/' , '\<hr/\>' , $subject_string );
// $outcome will be: "\<hr/\>section 1.1: Disability ........"
Any help would be gratefully received
Use
\bsection\s*\d+(?:\.\d+)*:
Replace with <hr/>$0. See regex proof.
EXPLANATION
--------------------------------------------------------------------------------
\b the boundary between a word char (\w) and
something that is not a word char
--------------------------------------------------------------------------------
section 'section'
--------------------------------------------------------------------------------
\s* whitespace (\n, \r, \t, \f, and " ") (0 or
more times (matching the most amount
possible))
--------------------------------------------------------------------------------
\d+ digits (0-9) (1 or more times (matching
the most amount possible))
--------------------------------------------------------------------------------
(?: group, but do not capture (0 or more times
(matching the most amount possible)):
--------------------------------------------------------------------------------
\. '.'
--------------------------------------------------------------------------------
\d+ digits (0-9) (1 or more times (matching
the most amount possible))
--------------------------------------------------------------------------------
)* end of grouping
--------------------------------------------------------------------------------
: ':'
Code snippet:
$re = '/\bsection\s*\d+(?:\.\d+)*:/';
$str = 'section 1.1: Disability ........';
$subst = '<hr/>$0';
$result = preg_replace($re, $subst, $str);
echo "The result of the substitution is ".$result;

PHP preg_replace remove specific parts from string

im having problems with understanding regex in PHP. I have img src:
src="http://example.com/javascript:gallery('/info/2005/image.jpg',383,550)"
and need to build from it this:
src="http://example.com/info/2005/image.jpg"
How it it possible to cut first and last part from string to obtain clear link without javascript part?
Right now im using this regex:
$cont = 'src="http://example.com/javascript:gallery('/info/2005/image.jpg',383,550)"'
$cont = preg_replace("/(src=\")(.*)(\/info)/","$1http://example.com$3", $cont);
and output is:
src="http://example.com/info/2005/image.jpg',383,550)"
As an alternative solution, you might also capture the src="http://example.com part by matching the protocol in group 1, so you can use it in the replacement.
(src="https?://[^/]+)/[^']*'(/info[^']*)'[^"]*
Explanation
(src="https?://[^/]+)/ Capture group 1, match src="http, optional s, :// and till the first /
[^']*' Match any char except ', then match '
(/info[^']*) Capture group 2, match /info followed by any char except '
'[^"]* Match the ' followed by matching any char except "
Regex demo | Php demo
$cont = 'src="http://example.com/javascript:gallery(\'/info/2005/image.jpg\',383,550)"';
$cont = preg_replace("~(src=\"https?://[^/]+)/[^']*'(/info[^']*)'[^\"]*~", '$1$2', $cont);
echo $cont;
Output
src="http://example.com/info/2005/image.jpg"
Use
preg_replace("/src=\"\K.*(\/info[^']*)'[^\"]*/", 'http://example.com$1', $cont)
See regex proof.
Explanation
--------------------------------------------------------------------------------
src= 'src='
--------------------------------------------------------------------------------
\" '"'
--------------------------------------------------------------------------------
\K match reset operator
--------------------------------------------------------------------------------
.* any character except \n (0 or more times
(matching the most amount possible))
--------------------------------------------------------------------------------
( group and capture to \1:
--------------------------------------------------------------------------------
\/ '/'
--------------------------------------------------------------------------------
info 'info'
--------------------------------------------------------------------------------
[^']* any character except: ''' (0 or more
times (matching the most amount possible))
--------------------------------------------------------------------------------
) end of \1
--------------------------------------------------------------------------------
' '\''
--------------------------------------------------------------------------------
[^\"]* any character except: '\"' (0 or more
times (matching the most amount possible))

PHP preg_replace find match in html but not if its a html attribute

I have two regex one which matches [value] and another which matches html attributes but i need to combine them into a single regex.
This is the regex I'm working with to find [value]
$tagregexp = '[a-zA-Z_\-][0-9a-zA-Z_\-\+]{2,}';
$pattern =
'\\[' // Opening bracket
. '(\\[?)' // 1: Optional second opening bracket for escaping shortcodes: [[tag]]
. "($tagregexp)" // 2: Shortcode name
. '(?![\\w-])' // Not followed by word character or hyphen
. '(' // 3: Unroll the loop: Inside the opening shortcode tag
. '[^\\]\\/]*' // Not a closing bracket or forward slash
. '(?:'
. '\\/(?!\\])' // A forward slash not followed by a closing bracket
. '[^\\]\\/]*' // Not a closing bracket or forward slash
. ')*?'
. ')'
. '(?:'
. '(\\/)' // 4: Self closing tag ...
. '\\]' // ... and closing bracket
. '|'
. '\\]' // Closing bracket
. '(?:'
. '(' // 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags
. '[^\\[]*+' // Not an opening bracket
. '(?:'
. '\\[(?!\\/\\2\\])' // An opening bracket not followed by the closing shortcode tag
. '[^\\[]*+' // Not an opening bracket
. ')*+'
. ')'
. '\\[\\/\\2\\]' // Closing shortcode tag
. ')?'
. ')'
. '(\\]?)'; // 6: Optional second closing bracket for escaping shortcodes: [[tag]]
example here
This regex (\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']? matches an attribute and a value.
example here
I would like the regex to match [value] in the following examples
<div [value] ></div>
<div>[value]</div>
but not find a match in this example
<input attr="attribute[value]"/>
Just need to make it into a single regex to use in my preg_replace_callback
preg_replace_callback($pattern, replace_matches, $html);
Foreward
On the surface it looks like you're attempting to parse html code with a regular expression. I feel obligated to point out that it's not advisable to use a regex to parse HTML due to all the possible obscure edge cases that can crop up, but it seems that you have some control over the HTML so you should able to avoid many of the edge cases the regex police cry about.
Description
<\w+\s(?=(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*?\[(?<DesiredValue>[^\]]*)\])
|
<\w+\s?(?:[^>=]|='[^']*'|="[^"]*"|=[^'"][^\s>]*)*>
(?:(?!<\/div>)(?!\[).)*\[(?<DesiredValue>[^\]]*)\]
This regular expression will do the following:
capture the substring inside square brackets [some value]
were [value] is in the attributes of a tag
were [value] is in not inside the attributes area of a tag
providing the substring is not nested inside another value <input attrib=" [value] ">
the captured substring will not include the wrapping square brackets
allow any tag name, or replace the \w with the desired tag names
allow value to be any string of characters
avoid difficult edge cases
Note: this regex is best used with the following flags:
global
dot matches new line
ignore white space in expression
allow duplicate named capture groups
Examples
Live Demo
https://regex101.com/r/tT0bN5/1
Sample Text
<div [value 1] ></div>
<div>[value 2]</div>
but not find a match in this example
<div attr="attribute[value 3]"/>
<img [value 4]>
[value 6]
Sample Matches
MATCH 1
DesiredValue [6-13] `value 1`
MATCH 2
DesiredValue [29-36] `value 2`
MATCH 3
DesiredValue [121-128] `value 4`
MATCH 4
DesiredValue [159-166] `value 6`
Explanation
NODE EXPLANATION
----------------------------------------------------------------------
<div '<div'
----------------------------------------------------------------------
\s whitespace (\n, \r, \t, \f, and " ")
----------------------------------------------------------------------
(?= look ahead to see if there is:
----------------------------------------------------------------------
(?: group, but do not capture (0 or more
times (matching the least amount
possible)):
----------------------------------------------------------------------
[^>=] any character except: '>', '='
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
=' '=\''
----------------------------------------------------------------------
[^']* any character except: ''' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
' '\''
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
=" '="'
----------------------------------------------------------------------
[^"]* any character except: '"' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
= '='
----------------------------------------------------------------------
[^'"] any character except: ''', '"'
----------------------------------------------------------------------
[^\s>]* any character except: whitespace (\n,
\r, \t, \f, and " "), '>' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
)*? end of grouping
----------------------------------------------------------------------
\[ '['
----------------------------------------------------------------------
( group and capture to \1:
----------------------------------------------------------------------
[^\]]* any character except: '\]' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
) end of \1
----------------------------------------------------------------------
\] ']'
----------------------------------------------------------------------
) end of look-ahead
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
<div '<div'
----------------------------------------------------------------------
\s? whitespace (\n, \r, \t, \f, and " ")
(optional (matching the most amount
possible))
----------------------------------------------------------------------
(?: group, but do not capture (0 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
[^>=] any character except: '>', '='
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
=' '=\''
----------------------------------------------------------------------
[^']* any character except: ''' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
' '\''
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
=" '="'
----------------------------------------------------------------------
[^"]* any character except: '"' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
= '='
----------------------------------------------------------------------
[^'"] any character except: ''', '"'
----------------------------------------------------------------------
[^\s>]* any character except: whitespace (\n,
\r, \t, \f, and " "), '>' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
)* end of grouping
----------------------------------------------------------------------
> '>'
----------------------------------------------------------------------
(?: group, but do not capture (0 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
(?! look ahead to see if there is not:
----------------------------------------------------------------------
< '<'
----------------------------------------------------------------------
\/ '/'
----------------------------------------------------------------------
div> 'div>'
----------------------------------------------------------------------
) end of look-ahead
----------------------------------------------------------------------
(?! look ahead to see if there is not:
----------------------------------------------------------------------
\[ '['
----------------------------------------------------------------------
) end of look-ahead
----------------------------------------------------------------------
. any character
----------------------------------------------------------------------
)* end of grouping
----------------------------------------------------------------------
\[ '['
----------------------------------------------------------------------
( group and capture to \2:
----------------------------------------------------------------------
[^\]]* any character except: '\]' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
) end of \2
----------------------------------------------------------------------
\] ']'

preg_replace to change url from relative to absolute

My PHP code is:
$string = preg_replace('/(href|src)="([^:"]*)(?:")/i','$1="http://mydomain.com/$2"', $string);
It work with:
- Link 1 => Link 1
- Link 1 => Link 1
But not with:
- <a href='aaa/'>Link 1</a>
- Link 1 (I don't want to change if url start by #).
Please help me!
How about:
$arr = array('Link 1',
'Link 1',
"<a href='aaa/'>Link 1</a>",
'Link 1');
foreach( $arr as $lnk) {
$lnk = preg_replace('~(href|src)=(["\'])(?!#)(?!http://)([^\2]*)\2~i','$1="http://mydomain.com/$3"', $lnk);
echo $lnk,"\n";
}
output:
Link 1
Link 1
Link 1
Link 1
Explanation:
The regular expression:
(?-imsx:(href|src)=(["\'])(?!#)(?!http://)([^\2]*)\2)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?-imsx: group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
----------------------------------------------------------------------
( group and capture to \1:
----------------------------------------------------------------------
href 'href'
----------------------------------------------------------------------
| OR
----------------------------------------------------------------------
src 'src'
----------------------------------------------------------------------
) end of \1
----------------------------------------------------------------------
= '='
----------------------------------------------------------------------
( group and capture to \2:
----------------------------------------------------------------------
["\'] any character of: '"', '\''
----------------------------------------------------------------------
) end of \2
----------------------------------------------------------------------
(?! look ahead to see if there is not:
----------------------------------------------------------------------
# '#'
----------------------------------------------------------------------
) end of look-ahead
----------------------------------------------------------------------
(?! look ahead to see if there is not:
----------------------------------------------------------------------
http:// 'http://'
----------------------------------------------------------------------
) end of look-ahead
----------------------------------------------------------------------
( group and capture to \3:
----------------------------------------------------------------------
[^\2]* any character except: '\2' (0 or more
times (matching the most amount
possible))
----------------------------------------------------------------------
) end of \3
----------------------------------------------------------------------
\2 what was matched by capture \2
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
This will work for you
PHP:
function expand_links($link) {
return('href="http://example.com/'.trim($link, '\'"/\\').'"');
}
$textarea = preg_replace('/href\s*=\s*(?<href>"[^\\"]*"|\'[^\\\']*\')/e', 'expand_links("$1")', $textarea);
I also changed the regex to work with either double quotes or apostrophes
try this for your pattern
/(href|src)=['"]([^"']+)['"]/i
the replacement stays as is
EDIT:
wait one...i didn't test on the first 2 link types, just the ones that didn't work...give me a moment
REVISISED:
sorry about the first regex, i forgot about the second example that worked with the domain in it
(href|src)=['"](?:http://.+/)?([^"']+)['"]
that should work

Match all occurrences of a string

My search text is as follows.
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
It contains many lines(actually a javascript file) but need to parse the values in variable strings , ie aaa , bbb, ccc , ddd , eee
Following is the Perl code, or use PHP at bottom
my $str = <<STR;
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR
my #matches = $str =~ /(?:\"(.+?)\",?)/g;
print "#matches";
I know the above script will match all instants, but it will parse strings ("xyz") in the other lines also. So I need to check the string var strings =
/var strings = \[(?:\"(.+?)\",?)/g
Using above regex it will parse aaa.
/var strings = \[(?:\"(.+?)\",?)(?:\"(.+?)\",?)/g
Using above, will get aaa , and bbb. So to avoid the regex repeating I used '+' quantifier as below.
/var strings = \[(?:\"(.+?)\",?)+/g
But I got only eee, So my question is why I got eee ONLY when I used '+' quantifier?
Update 1: Using PHP preg_match_all (doing it to get more attention :-) )
$str = <<<STR
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR;
preg_match_all("/var strings = \[(?:\"(.+?)\",?)+/",$str,$matches);
print_r($matches);
Update 2: Why it matched eee ? Because of the greediness of (?:\"(.+?)\",?)+ . By removing greediness /var strings = \[(?:\"(.+?)\",?)+?/ aaa will be matched. But why only one result? Is there any way it can be achieved by using single regex?
Here's a single-regex solution:
/(?:\bvar\s+strings\s*=\s*\[|\G,)\s*"([^"]*)"/g
\G is a zero-width assertion that matches the position where the previous match ended (or the beginning of the string if it's the first match attempt). So this acts like:
var\s+strings\s*=\s*[\s*"([^"]*)"
...on the first attempt, then:
,\s*"([^"]*)"
...after that, but each match has to start exactly where the last one left off.
Here's a demo in PHP, but it will work in Perl, too.
You may prefer this solution which first looks for the string var strings = [ using the /g modifier. This sets \G to match immediately after the [ for the next regex, which looks for all immediately following occurrences of double-quoted strings, possibly preceded by commas or whitespace.
my #matches;
if ($str =~ /var \s+ strings \s* = \s* \[ /gx) {
#matches = $str =~ /\G [,\s]* "([^"]+)" /gx;
}
Despite using the /g modifier your regex /var strings = \[(?:\"(.+?)\",?)+/g matches only once because there is no second occurrence of var strings = [. Each match returns a list of the values of the capture variables $1, $2, $3 etc. when the match completed, and /(?:"(.+?)",?)+/ (there is no need to escape the double-quotes) captures multiple values into $1 leaving only the final value there. You need to write something like the above , which captures only a single value into $1 for each match.
Because the + tells it to repeat the exact stuff inside brackets (?:"(.+?)",?) one or more times. So it will match the "eee" string, end then look for repetitions of that "eee" string, which it does not find.
use YAPE::Regex::Explain;
print YAPE::Regex::Explain->new(qr/var strings = \[(?:"(.+?)",?)+/)->explain();
The regular expression:
(?-imsx:var strings = \[(?:"(.+?)",?)+)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?-imsx: group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
----------------------------------------------------------------------
var strings = 'var strings = '
----------------------------------------------------------------------
\[ '['
----------------------------------------------------------------------
(?: group, but do not capture (1 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
( group and capture to \1:
----------------------------------------------------------------------
.+? any character except \n (1 or more
times (matching the least amount
possible))
----------------------------------------------------------------------
) end of \1
----------------------------------------------------------------------
" '"'
----------------------------------------------------------------------
,? ',' (optional (matching the most amount
possible))
----------------------------------------------------------------------
)+ end of grouping
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
A simpler example would be:
my #m = ('abcd' =~ m/(\w)+/g);
print "#m";
Prints only d. This is due to:
use YAPE::Regex::Explain;
print YAPE::Regex::Explain->new(qr/(\w)+/)->explain();
The regular expression:
(?-imsx:(\w)+)
matches as follows:
NODE EXPLANATION
----------------------------------------------------------------------
(?-imsx: group, but do not capture (case-sensitive)
(with ^ and $ matching normally) (with . not
matching \n) (matching whitespace and #
normally):
----------------------------------------------------------------------
( group and capture to \1 (1 or more times
(matching the most amount possible)):
----------------------------------------------------------------------
\w word characters (a-z, A-Z, 0-9, _)
----------------------------------------------------------------------
)+ end of \1 (NOTE: because you are using a
quantifier on this capture, only the LAST
repetition of the captured pattern will be
stored in \1)
----------------------------------------------------------------------
) end of grouping
----------------------------------------------------------------------
If you use the quantifier on the capture group, only the last instance will be used.
Here's a way that works:
my $str = <<STR;
...
...
var strings = ["aaa","bbb","ccc","ddd","eee"];
...
...
STR
my #matches;
$str =~ m/var strings = \[(.+?)\]/; # get the array first
my $jsarray = $1;
#matches = $array =~ m/"(.+?)"/g; # and get the strings from that
print "#matches";
Update:
A single-line solution (though not a single regex) would be:
#matches = ($str =~ m/var strings = \[(.+?)\]/)[0] =~ m/"(.+?)"/g;
But this is highly unreadable imho.

Categories