If you just want to convert '&' in content and avoid tags (ie: attribute values)
and avoid script blocks, something like below will work for most occurances.
However, it should be noted that attribute values should be converted as well.
And doing that requires a lot more work.
Working sample http://www.ideone.com/9MhCq
<?php
$html=<<<EOD
<some &ta&g> S&P &&more; and &some; <more> ¬me;
&& Ñ ꤯
<script flavor?>
val && this & this
</script>
&
EOD;
$rxent = '(?:&(?:[A-Za-z_:][\w:.-]*|\#(?:[0-9]+|x[0-9a-fA-F]+));)';
$rxtag =
'<
(?:
\?php\s+.*?\?
| (?:
(?:
(?:script|style)\s*
| (?:script|style)\s+(?:".*?"|\'.*?\'|[^>]*?)+\s*
)> .*? </(?:script|style)\s*
)
| (?:
/?[A-Za-z_:][\w:.-]*\s*/?
| [A-Za-z_:][\w:.-]*\s+(?:".*?"|\'.*?\'|[^>]*?)+\s*/?
| !(?:DOCTYPE.*?|--.*?--)
)
)
>
';
$rxmain = "~(?xs:((?:$rxtag)+) | ((?!$rxent)&))~";
print "$html\n\n";
$html = preg_replace_callback($rxmain, 'fixamp_cb', $html);
print "$html\n";
function fixamp_cb( $matches ) {
# Return tags and script blocks unchanged.
if (isset($matches[1]) && $matches[1])
return $matches[1];
return '&';
}
?>
Answer from user557597 on Stack OverflowIf you just want to convert '&' in content and avoid tags (ie: attribute values)
and avoid script blocks, something like below will work for most occurances.
However, it should be noted that attribute values should be converted as well.
And doing that requires a lot more work.
Working sample http://www.ideone.com/9MhCq
<?php
$html=<<<EOD
<some &ta&g> S&P &&more; and &some; <more> ¬me;
&& Ñ ꤯
<script flavor?>
val && this & this
</script>
&
EOD;
$rxent = '(?:&(?:[A-Za-z_:][\w:.-]*|\#(?:[0-9]+|x[0-9a-fA-F]+));)';
$rxtag =
'<
(?:
\?php\s+.*?\?
| (?:
(?:
(?:script|style)\s*
| (?:script|style)\s+(?:".*?"|\'.*?\'|[^>]*?)+\s*
)> .*? </(?:script|style)\s*
)
| (?:
/?[A-Za-z_:][\w:.-]*\s*/?
| [A-Za-z_:][\w:.-]*\s+(?:".*?"|\'.*?\'|[^>]*?)+\s*/?
| !(?:DOCTYPE.*?|--.*?--)
)
)
>
';
$rxmain = "~(?xs:((?:$rxtag)+) | ((?!$rxent)&))~";
print "$html\n\n";
$html = preg_replace_callback($rxmain, 'fixamp_cb', $html);
print "$html\n";
function fixamp_cb( $matches ) {
# Return tags and script blocks unchanged.
if (isset($matches[1]) && $matches[1])
return $matches[1];
return '&';
}
?>
Why not something simple like:
$html = preg_replace('/([^&])&([^&])/', '$1&$2', $html);
If you want to avoid replacing all ampersands inside Javascript, add lookahead/behind for script tags, or first split the document on script tags, and only run the replace on the non-script parts.
$html = preg_split('/<\/?script>/', $html);
foreach ($html as
v) {
if ($piece[0] == "<") {
$html[$k] = preg_replace('/([^&])&([^&])/', '$1&$2', $v);
} else {
$html[$k] = "<script>" . $html[$k] . "</script>";
}
This will need some modification if your script tags have attributes.
If you're sanitizing user content, then you might be better using the tools that are already available. See HTML Purifier
PHP negative lookahead regex not working - Stack Overflow
preg match - PCRE regex lookahead - Stack Overflow
PHP regex lookahead not working as expected - Stack Overflow
php - Negative Look-back in preg_replace() with numbered Backreferences, has no effect - Stack Overflow
Actually you need to escape $ character by a backslash \ in your pattern. Also to make it more confident, you can add \s* which means zero or more white space(s) before = character.
Use this pattern:
/\$search(?!\s*\=)/i
Online Demo
/delimiter\$matches$literallysearchmatches that word literally(?!negative Lookahead which checks being both white space(s) (if exists) and=after that variable name./imodifire makes the pattern insensitive (there isn't any different betweena-zandA-Z)
Note: You have to use single quotes '/pattern/' for pattern above. If you want to use double quotes " then you should escape backslashes again:
/\\$search(?!\\s*\\=)/i
The $ is an anchor in regex to match the end of a line, so since it's a special character you have to escape it.
You can use:
preg_replace("/(\\$search)(?!=)/i", $replacement, $string, -1, $count);
or
preg_replace('/(\$search)(?!=)/i', $replacement, $string, -1, $count);
Regex demo
If you're looking to compare the version, you can strip on the space and then use version_compare().
If you just want the numeric representation, use a regex to simply use preg_replace() all non digits in the original version string.
$version = '2.3.3 Release';
echo preg_replace('/\D+/', '', $version);
This seemed to work for all my test cases.
preg_replace('/^(\d+)\.(\d+)\.(\d+).*$/', '$1$2$3', $version);
I suggest this regex that doesn't require any anchoring (thus can be used on a page containing links and text):
<a href\s?=\s?"http(s)?://([^"]+)">([^<]+)</a>|(http)?(s)?(://)?((?:[-\w]+\.)+\S+[^,.\s])
Usage:
$result = preg_replace('~<a href\s?=\s?"http(s)?://([^"]+)">([^<]+)</a>|(http)?(s)?(://)?((?:[-\w]+\.)+\S+[^,.\s])~', '<a href="http$1$5://$2$7">$3$4$5$6$7</a>', $text);
The regex matches both the 'normal' urls and the ones that are already wrapped between anchor tags, but treats them differently in the replace.
Use the below regex to embed a site address inside <a> tag and don't do the embeding on already embeded site adresses.
Regular expression:
^(?!<a href.*$)(http)?(s)?(:\/\/)?(([-\w]+\.)+([^\s]+)+[^,.\s])
Substitution:
<a href="http$2://$4">$1$2$3$4</a>
DEMO
PHP code:
<?php
$mystring = <<<EOT
http://www.example.com
https://www.example.com
www.example.com
<a href="http://www.example.com">http://www.example.com</a>
EOT;
$pattern = "~^(?!<a href.*$)(http)?(s)?(:\/\/)?(([-\w]+\.)+([^\s]+)+[^,.\s])~m";
$replacement = '<a href="http$2://$4">$1$2$3$4</a>';
echo preg_replace($pattern, $replacement, $mystring);
?>
Output:
<a href="http://www.example.com">http://www.example.com</a>
<a href="https://www.example.com">https://www.example.com</a>
<a href="http://www.example.com">www.example.com</a>
<a href="http://www.example.com">http://www.example.com</a>
You mixed lookahead and lookbehind positions.
The (?![\'"])(\w+) is equal to (\w+) because (?![\'"]) is a negative lookahead and requires the next char not to be a ' or ", but since the next pattern is \w, matching a word char, the lookahead becomes redundant. You need to use a negative lookbehind here, (?<![\'"]) (\w+). And the problem with (\w+)(?<![\'"]) is similar: the word char cannot be a ' and " and the negative lookbehind is redundant. You wanted a lookahead here.
You need to use
'~(?:^|\b) (?<![\'"]) (\w+) (?![\'"]) :~mx'
See the regex demo.
As posted by Wiktor in the comments to my initial question, this is the solution:
'~(?:^|\b) (?<![\'"]) (\w+) (?![\'"]) :~mx'
I have arrays (I condensed it for reddit) that are used to find & replace certain words that a user inputs in a text area.
$PATTERNS = array('/\Wdc\W/i', '/\Wtr\W/i') $RESULTS = array('ab', 'ef')
For the first entry in the $PATTERNS array, I'd like to search what the user inputs and replace text like '6dc', '12dc', '.dc', 'dc', ':dc', etc. However, I need the numbers and punctuation to stay when the dc portion is being replaced by 'ab'. I added the boundaries at the front and end as I don't want to make any changes if there are letters at the front and end, so words like 'padc' and 'dcom' would stay the same.
Example input -> output goal:
6dc -> 6ab
dc5 -> ab5
.dc -> .ab
dc. -> ab.
pdc -> pdc (no change)
*dc -> *ab
dc* -> ab*
Incorrect output I'm getting from my current code:
6dc -> ab
dc5 -> ab
.dc -> ab
dc. -> ab
What I've tried:
I've looked at incorporating in a lookbehind/lookahead and (*SKIP) (*FAIL), but I can't seem to figure it out.