user2693053 user2693053 - 6 days ago 3
PHP Question

How to match regex untill 2 linebreaks

I'm trying to strip metadata from some news articles I got from Lexis Nexis. I'm currently having some trouble with stripping multiline meta data, that is: my pattern only matches the first line.

I suspect this could be fixed with a negative lookahead that looks for multiple line breaks, but so far I've not been able to work it out.

$article = "SECTION: Media; Blz. 33

LENGTH: 355 woorden

HIGHLIGHT: Verkiezingen Robert Jensen keert vanaf vandaag terug op de
Nederlandse televisie. In 'Jensen kiest voor Amerika' onderzoekt hij de
aantrekkingskracht van Donald Trump. Is hij definitief terug of lonkt misschien
een oude liefde?

Waarom ben je Jensen kiest voor Amerika gaan maken?

,,Ik vind Amerika al mijn hele leven een fascinerend land, ben er vaak en volg
de verkiezingen als fenomeen. Toen Trump maar bleef winnen, begon het idee voor
een tv-programma te leven. Ik wilde alles van die Trump-aanhangers weten want
daar hoor je weinig over.''";

preg_match_all( "/[A-Z\h-]+: *[^\r]+/", $article, $matches);

var_dump( $matches[0] );
die();


Results in:

array(3) { [0]=> string(23) "SECTION: Media; Blz. 33" [1]=> string(19) "LENGTH: 355 woorden" [2]=> string(70) "HIGHLIGHT: Verkiezingen Robert Jensen keert vanaf vandaag terug op de" }


As you can only the first line of the highlight data is matched.

I've also put a full article (with multiple examples of multiline metadata) up on regex101.

Edit:



After two answers I realise that my question may not have been totally clear -- sorry about that.

I'm trying to strip/remove the meta data from
$article
so that only the body of the article is left. In my example I'm using
preg_match_all()
. Once I can correctly match the meta tags, I'll use
preg_replace()
to replace it with an empty string. I considered using preg_split(), but than I'd have to guess which of the array items is the actual body -- a meta tag could be longer then the actual body, although that's not a likely scenario.

So what is meta tag? A meta tag always starts with
[A-Z\h-]+:
and ends with a blank line, i.e. two line breaks.

If you take the aforementioned example and put the tags in an array and the article body in a separate variable it'd look like this:

<?php
$meta_tags = [
"SECTION" => "Media; Blz. 33",
"LENGTH" => "355 woorden",
"HIGHLIGHT" => "Verkiezingen Robert Jensen keert vanaf vandaag terug op de
Nederlandse televisie. In 'Jensen kiest voor Amerika' onderzoekt hij de
aantrekkingskracht van Donald Trump. Is hij definitief terug of lonkt misschien
een oude liefde?"
];

$article_body = "Waarom ben je Jensen kiest voor Amerika gaan maken?

,,Ik vind Amerika al mijn hele leven een fascinerend land, ben er vaak en volg
de verkiezingen als fenomeen. Toen Trump maar bleef winnen, begon het idee voor
een tv-programma te leven. Ik wilde alles van die Trump-aanhangers weten want
daar hoor je weinig over.''";


Additional note: There may also be some more meta tags after the body's article, but the
preg_replace()
on meta tags should also take care of that.

Answer

Since you have no complex matching to do, you could also go with a simple preg_split() that targets the metadata keys as delimiters, and not worry about look-aheads etc.:

$matches = preg_split( '/\v*([A-Z\h-]+):\h?/', $article, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);

Gives you:

array(6) {
  [0]=> string(7) "SECTION"
  [1]=> string(14) "Media; Blz. 33"
  [2]=> string(6) "LENGTH"
  [3]=> string(11) "355 woorden"
  [4]=> string(9) "HIGHLIGHT"
  [5]=> string(556) "Verkiezingen Robert Jensen keert vanaf vandaag terug op de
Nederlandse televisie. In 'Jensen kiest voor Amerika' onderzoekt hij de
aantrekkingskracht van Donald Trump. Is hij definitief terug of lonkt misschien
een oude liefde?

Waarom ben je Jensen kiest voor Amerika gaan maken?
/...snip/"
}

... which I'm sure you can process onward easily enough, evens are keys and odds are values. ^_^


But if you do want to use a look-ahead, here's an example:

preg_match_all('/[A-Z\h-]+:.*?(?=\s*(?:[A-Z\h-]+:|\Z))/s', $article, $matches);

This matches anything that starts with your meta-key, followed by anything that's followed by another meta-key or the end of your data. Better not worry about matching line-breaks here, match what matters instead. The look-ahead could be simplified further to simply (?=[A-Z\h-]+:|\Z), but the \s* trims the trailing space in the values. (And that's why the subsequent actual look-ahead group is specified as ?: non-capture, we don't want to clutter the output now do we.) Result:

array(3) {
  [0]=> string(23) "SECTION: Media; Blz. 33"
  [1]=> string(19) "LENGTH: 355 woorden"
  [2]=> string(567) "HIGHLIGHT: Verkiezingen Robert Jensen keert vanaf vandaag terug op de
Nederlandse televisie. In 'Jensen kiest voor Amerika' onderzoekt hij de
aantrekkingskracht van Donald Trump. Is hij definitief terug of lonkt misschien
een oude liefde?

Waarom ben je Jensen kiest voor Amerika gaan maken?
/...snip/"
}

But if you want to make it nice, rather do this:

preg_match_all('/([A-Z\h-]+):\h*(.*?)(?=\s*(?:[A-Z\h-]+:|\Z))/s', $article, $matches);
$parsed = array_combine($matches[1], $matches[2]);

That is, we're adding capture-groups for the key and the value, and then combining them into an associative array. (Do check if you have matches before trying to combine.) Result:

array(3) {
  ["SECTION"]=> string(14) "Media; Blz. 33"
  ["LENGTH"]=> string(11) "355 woorden"
  ["HIGHLIGHT"]=> string(556) "Verkiezingen Robert Jensen keert vanaf vandaag terug op de
Nederlandse televisie. In 'Jensen kiest voor Amerika' onderzoekt hij de
aantrekkingskracht van Donald Trump. Is hij definitief terug of lonkt misschien
een oude liefde?

Waarom ben je Jensen kiest voor Amerika gaan maken?
/...snip/"
}

Edit If all you really want is the body text (now that it's clear where the body text begins: after a one-para highlight meta), and you have no use for the metadata, then just clean up the article with preg_replace matching all sections that start with the meta-key, and match everything 'til the first double (or more) linebreak. (Use \v to match any vertical space.)

$article = preg_replace('/([A-Z\h-]+):\h*(.*?)[\v]{2,}/s', '', $article);
Comments