I can't seem to figure out the proper regular expression for extracting just specific numbers from a string. I have an HTML string that has various img tags in it. There are a bunch of img tags in the HTML that I want to extract a portion of the value from. They follow this format:
<img src="http://domain.com/images/59.jpg" class="something" />
<img src="http://domain.com/images/549.jpg" class="something" />
<img src="http://domain.com/images/1249.jpg" class="something" />
<img src="http://domain.com/images/6.jpg" class="something" />
So, varying lengths of numbers before what 'usually' is a .jpg (it may be a .gif, .png, or something else too). I want to only extract the number from that string.
The 2nd part of this is that I want to use that number to look up an entry in a database and grab the alt/title tag for that specific id of image. Lastly, I want to add that returned database value into the string and throw it back into the HTML string.
Any thoughts on how to proceed with it would be great...
Thus far, I've tried:
$pattern = '/img src="http://domain.com/images/[0-9]+\/.jpg';
preg_match_all($pattern, $body, $matches);
var_dump($matches);
I think this is the best approach:
Use an HTML parser to extract the image tags
Use a regular expression (or perhaps string manipulation) to extract the ID
Query for the data
Use the HTML parser to insert the returned data
Here is an example. There are improvements I can think of, such as using string manipulation instead of a regex.
$html = '<img src="http://domain.com/images/59.jpg" class="something" />
<img src="http://domain.com/images/549.jpg" class="something" />
<img src="http://domain.com/images/1249.jpg" class="something" />
<img src="http://domain.com/images/6.jpg" class="something" />';
$doc = new DOMDocument;
$doc->loadHtml( $html);
foreach( $doc->getElementsByTagName('img') as $img)
{
$src = $img->getAttribute('src');
preg_match( '#/images/([0-9]+)\.#i', $src, $matches);
$id = $matches[1];
echo 'Fetching info for image ID ' . $id . "\n";
// Query stuff here
$result = 'Got this from the DB';
$img->setAttribute( 'title', $result);
$img->setAttribute( 'alt', $result);
}
$newHTML = $doc->saveHtml();
Using regular expressions, you can get the number really easily. The third argument for preg_match_all is a by-reference array that will be populated with the matches that were found.
preg_match_all('/<img src="http:\/\/domain.com\/images\/(\d+)\.[a-zA-Z]+"/', $html, $matches);
print_r($matches);
This would contain all of the stuff that it found.
Consider using preg_replace_callback.
Use this regex: (images/([0-9]+)[^"]+")
Then, as the callback argument, use an anonymous function. Result:
$output = preg_replace_callback(
"(images/([0-9]+)[^\"]+\")",
function($m) {
// $m[1] is the number.
$t = getTitleFromDatabase($m[1]); // do whatever you have to do to get the title
return $m[0]." title=\"".$t."\"";
},
$input
);
use preg_match_all:
preg_match_all('#<img.*?/(\d+)\.#', $str, $m);
print_r($m);
output:
Array
(
[0] => Array
(
[0] => <img src="http://domain.com/images/59.
[1] => <img src="http://domain.com/images/549.
[2] => <img src="http://domain.com/images/1249.
[3] => <img src="http://domain.com/images/6.
)
[1] => Array
(
[0] => 59
[1] => 549
[2] => 1249
[3] => 6
)
)
This regex should match the number parts:
\/images\/(?P<digits>[0-9]+)\.[a-z]+
Your $matches['digits'] should have all of the digits you want as an array.
Regular expressions alone are a bit on the loosing ground when it comes to parsing crappy HTML. DOMDocument's HTML handling is pretty well to serve tagsoup hot and fresh, xpath to select your image srcs and a simple sscanf to extract the number:
$ids = array();
$doc = new DOMDocument();
$doc->loadHTML($html);
foreach(simplexml_import_dom($doc)->xpath('//img/#src[contains(., "/images/")]') as $src) {
if (sscanf($src, '%*[^0-9]%d', $number)) {
$ids[] = $number;
}
}
Because that only gives you an array, why not encapsulate it?
$html = '<img src="http://domain.com/images/59.jpg" class="something" />
<img src="http://domain.com/images/549.jpg" class="something" />
<img src="http://domain.com/images/1249.jpg" class="something" />
<img src="http://domain.com/images/6.jpg" class="something" />';
$imageNumbers = new ImageNumbers($html);
var_dump((array) $imageNumbers);
Which gives you:
array(4) {
[0]=>
int(59)
[1]=>
int(549)
[2]=>
int(1249)
[3]=>
int(6)
}
By that function above nicely wrapped into an ArrayObject:
class ImageNumbers extends ArrayObject
{
public function __construct($html) {
parent::__construct($this->extractFromHTML($html));
}
private function extractFromHTML($html) {
$numbers = array();
$doc = new DOMDocument();
$preserve = libxml_use_internal_errors(TRUE);
$doc->loadHTML($html);
foreach(simplexml_import_dom($doc)->xpath('//img/#src[contains(., "/images/")]') as $src) {
if (sscanf($src, '%*[^0-9]%d', $number)) {
$numbers[] = $number;
}
}
libxml_use_internal_errors($preserve);
return $numbers;
}
}
If your HTML should be that malformatted that even DOMDocument::loadHTML() can't handle it, then you only need to handle that internally in the ImageNumbers class.
$matches = array();
preg_match_all('/[:digits:]+/', $htmlString, $matches);
Then loop through the matches array to both reconstruct the HTML and to do you look up in the database.
Related
I am using preg_replace to delete from $content certain <img>:
$content=preg_replace('/(?!<img.+?id="img_menu".*?\/>)(?!<img.+?id="featured_img".*?\/>)<img.+?\/>/','',$content);
When I am now displaying the content using wordpress the_content function, I did indeed remove the <img>s from $content:
I'd like beforehand to get this images to place them elsewhere in the template. I am using the same regex pattern with preg_match_all:
preg_match_all('/(?!<img.+?id="img_menu".*?\/>)(?!<img.+?id="featured_img".*?\/>)<img.+?\/>/', $content, $matches);
But I can't get my imgs?
preg_match_all('/(?!<img.+?id="img_menu".*?\/>)(?!<img.+?id="featured_img".*?\/>)<img.+?\/>/', $content, $matches);
print_r($matches);
Array ( [0] => Array ( ) )
assuming and hopefully you are using php5, this is a task for DOMDocument and xpath. regex with html elements mostly will work, but check the following example from
<img alt=">" src="/path.jpg" />
regex will fail. since there aren't many guarantees in programming, take the guarantee that xpath will find EXACTLY what you want, at a perfomance cost, so to code it:
$doc = new DOMDocument();
$doc->loadHTML('<span><img src="com.png" /><img src="com2.png" /></span>');
$xpath = new DOMXPath($doc);
$imgs = $xpath->query('//span/img');
$html = '';
foreach($imgs as $img){
$html .= $doc->saveXML($img);
}
now you have all img elements in $html, use str_replace() to remove them from $content, and from there you can have a drink and be pleased that xpath with html elements is painless, just a little slower
ps. i couldnt be be bother understanding your regex, i just think xpath is better in your situation
at the end i have used preg_replace_callback:
$content2 = get_the_content();
$removed_imgs = array();
$content2 = preg_replace_callback('#(?!<img.+?id="featured_img".*?\/>)(<img.+? />)#',function($r) {
global $removed_imgs;
$removed_imgs[] = $r[1];
return '';
},$content2);
foreach($removed_imgs as $img){
echo $img;
}
How do I take this:
<img class="classone twoclass alignLEFT" src="xxxx" />
search for the word "align" in the class array, take the remainder of the word "align" (in this case "left") and assign it to an actual align property?
<img class="classone twoclass alignLEFT" align="LEFT" src="xxxx" />
I know I need
$needle = "align";
$haystack = "<img class="classone twoclass alignLEFT" src="xxxx" />"
and what I'm looking for is
$pincushion = {{the rest of the word from $needle}}
so basically I'm doing a preg_match for $needle. If found, how do I get the rest of that word (i.e., $pincushion) ?
I tried preg_split but it wouldn't allow me to use "align" as a delimiter. (makes sense)
This can NOT be jquery / javascript - it must take place in the rendered html code.
Any thoughts? I've spent 10 hours now searching for an answer with no real luck.
I did come across DomDocument but couldn't make that find what I needed either.
preg_match_all('#align(.*?)(" )#si', '<img class="classone twoclass alignLEFT" src="xxxx" />', $arr, PREG_PATTERN_ORDER);
Result:
Array
(
[0] => Array
(
[0] => alignLEFT"
)
[1] => Array
(
[0] => LEFT
)
[2] => Array
(
[0] => "
)
)
Explaination:
#align(.*?)(" )#si looks for algin followed by n-chars delimited by " or .
Since you're working with HTML you can get DOMDocument to work. It's a bit more drawn out, but probably easier to read and change than a complicated regex
There are libraries out there that are better at dealing with HTML fragments than domdocument, but if you want to use built in functions, domdocument is the way to go.
<?php
$html = '<img class="" src="xxxx" />adasdasdasd<img class="classone twoclass alignLEFT" src="xxxx" />';
$domdoc = new DOMDocument('');
$domdoc->loadHTML($html);
$xpath = new DOMXpath($domdoc);
$imgs = $xpath->query('//img');
foreach ($imgs as $element) {
$class = $element->getAttribute('class');
if (strpos($class, 'alignLEFT') !== false) {
$element->setAttribute('align', 'left');
}
}
// DOM Document works with full html documents, so now we have to isolate our fragment
$bodyelement = $xpath->query('/html/body');
$bodyhtml = $domdoc->saveXML($bodyelement->item(0));
echo str_replace(array('<body>', '</body>'), '', $bodyhtml);
I have this string:
<img src=images/imagename.gif alt='descriptive text here'>
and I am trying to split it up into the following two strings (array of two strings, what ever, just broken up).
imagename.gif
descriptive text here
Note that yes, it's actually the < and not <. Same with the end of the string.
I know regex is the answer, but I'm not good enough at regex to know how to pull it off in PHP.
Try this:
<?php
$s="<img src=images/imagename.gif alt='descriptive text here'>";
preg_match("/^[^\/]+\/([^ ]+)[^']+'([^']+)/", $s, $a);
print_r($a);
Output:
Array
(
[0] => <img src=images/imagename.gif alt='descriptive text here
[1] => imagename.gif
[2] => descriptive text here
)
Better use DOM xpath rather than regex
<?php
$your_string = html_entity_decode("<img src=images/imagename.gif alt='descriptive text here'>");
$dom = new DOMDocument;
$dom->loadHTML($your_string);
$x = new DOMXPath($dom);
foreach($x->query("//img") as $node)
{
echo $node->getAttribute("src");
echo $node->getAttribute("alt");
}
?>
I have this code that extracts the first image from an article in joomla:
<?php preg_match('/<img (.*?)>/', $this->article->text, $match); ?>
<?php echo $match[0]; ?>
Is there a way to extract all the images that are available in the article and not only one?
I may suggest first to not use Regular Expressions to parse HTML. You should use an appropiate parser such as DOMDocument::loadHTML which uses libxml.
Then you may query for the desired tags you want. Something like this may work (untested):
$doc = new DOMDocument;
$doc->loadHTML($htmlSource);
$xpath = new DOMXPath($doc);
$query = '//img';
$entries = $xpath->query($query);
foreach ($entries as $entry) {
// $entry->getAttribute('src')
}
Use preg_match_all. And you'll want to modify the pattern like so to take into account the trailing '/' inside the img tag.
$str = '<img src="asdf" />stuff more stuff <img src="qwerty" />';
preg_match_all('/<img (.*?)\/>/', $str, $matches);
print_r($matches);
Array
(
[0] => Array
(
[0] => <img src="asdf" />
[1] => <img src="qwerty" />
)
[1] => Array
(
[0] => src="asdf"
[1] => src="qwerty"
)
)
I've got img tag in my text and I want to get the name of the file from src
So I use this code
preg_match_all("|\/img\/(.*)\/>|U", $article_header, $matches, PREG_PATTERN_ORDER);
echo "match=".$matches[1][0]."<br/>";
Doing so I get this as a result
match=500.JPG\" alt=\"\" width=\"500\" height=\"360\"
So in this case I use "\/>" which means the end of tag.
But I want only name of the file "500.JPG" So I must use "\" but when I do it
preg_match_all("|\/img\/(.*)\\|U", $article_header, $matches, PREG_PATTERN_ORDER);
I get no matches :(
Please help
With the help of yes123 I did this
$doc = new DOMDocument();
$doc->loadHTML($article_header);
$imgs = $doc->getElementsByTagName('img');
$img_src = array();
foreach ($imgs as $img) {
// Store the img src
$img_src[] = $img->getAttribute('src');
echo $img_src[0];
}
which gives me this
\"sources/public/users/qqqqqq/articles/2011-06-11/7/img/500.JPG\"
But now anyway I want only 500.JPG from this
So what is the right regexp ?
To match a real backslash-char in regex, you have to 'double-escape' it, that means 4 backslashes to match a single backslash: \\\\
preg_match_all("|/img/(.*)\\\\|U", ...);
preg_match_all('/<img[^>*]src="([^"]+)".*>/Uis', $article_header, $matches)
You can't parse HTML with regex.
Use DOMDocument
// HTML already parsed into $dom
$imgs = $dom->getElementsByTagName('img');
$img_src = array();
foreach ($imgs as $img) {
// Store the img src
$img_src[] = $img->getAttribute('src');
}
Don't forget you can always search google or stackoverflow before opening a question
Try something like, I tested it now:
$article_header = 'foo <img src=\\"sources/public/users/qqqqqq/articles/2011-06-11/7/img/500.JPG\\" /> foo';
preg_match_all('|<img[^>]+?src="[^"]*?([^/"]+?)"|', stripslashes($article_header), $matches, PREG_PATTERN_ORDER);
echo "match=".$matches[1][0]."<br/>";
It seems that you have $article_header with slashes (that was a bit irritating), so I added an stripslashes().
use php function pathinfo
http://php.net/manual/en/function.pathinfo.php
pathinfo($img_src[0]);
result
Array
(
[dirname] => sources/public/users/qqqqqq/articles/2011-06-11/7/img/
[basename] => 500.JPG
[extension] => JPG
[filename] => 500
)