Pringles
Pringles

Reputation: 4615

Get number before the percent sign

I have a string like that:

$string = "Half Board, 10% Off & 100 Euro Star - Save £535";

The percentage can be anywhere in the string.

This is what I have, but I don't like the fact that it has a preg_replace AND a loop operation, which are heavy. I'd like a ReGex expression that would do it in one operation.

$string = "Half Board, 10% Off & 100 Euro Star - Save £535";
$string_array = explode(" ", $string);
$pattern = '/[^0-9,.]*/'; // non-digits

foreach($string_array as $pc) {

    if(stristr($pc, '%')) {

        $percent = preg_replace($pattern, '', $pc);
        break;
    }
}

echo $percent;
exit;

Upvotes: 2

Views: 3881

Answers (4)

Elias Van Ootegem
Elias Van Ootegem

Reputation: 76413

Update:
From the code you added to your question, I get the impression your percentages might look like 12.3% or even .50%. In which case, the regex you're looking for is this:

if (preg_match_all('/(\d+|\d+[.,]\d{1,2})(?=\s*%)/','some .50% and 5% text with 12.5% random percentages and 123 digits',$matches))
{
    print_r($matches);
}

Which returns:

Array
(
[0] => Array
    (
        [0] => .50
        [1] => 5
        [2] => 12.5
    )

[1] => Array
    (
        [0] => .50
        [1] => 5
        [2] => 12.5
    )

)

the expression explained:

  • (\d+|\d*[.,]\d{1,2}): is an OR -> either match digits \d+, or \d* zero or more digits, followed by a decimal separator ([.,]) and 1 or 2 digits (\d{1,2})
  • (?=\s*%): only if the afore mentioned group is followed by zero or more spaces and a % sign

Using a regular expression, with a positive lookahead, you can get exactly what you want:

if (preg_match_all('/\d+(?=%)/', 'Save 20% if you buy 5 iPhone charches (excluding 9% tax)', $matches))
{
    print_r($matches[0]);
}

gives you:

array (
    0 => '20',
    1 => '9'
)

Which is, I believe, what you are looking for
The regex works like this:

  • \d+ matches at least 1 digit (as many as possible)
  • (?=%): provided they are followed by a % sign

Because of the lookahead, the 5 isn't matched in the example I gave, because it's followed by a space, not a % sign.
If your string might be malformed (have any number of spaces between the digit and the % sign) a lookahead can deal with that, too. As ridgerunner pointed out to me, only lookbehinds need to be of fixed size, so:

preg_match_all('/\d+(?=\s*%)/', $txt, $matches)

The lookahead works like this

  • \s*: matches zero or more whitespace chars
  • %: and percent sign

Hence, both 123 % and 123% fit the pattern, and will match.
A good place to read up on regex's is regular-expressions.info


If "complex" regex's (ie with lookaround assertions) aren't your cup of tea (yet, though I strongly suggest learning to use them), you could resort to splitting the string:

$parts = array_map('trim', explode('%', $string));
$percentages = array();
foreach($parts as $part)
{
    if (preg_match('/\d+$/', $part, $match))
    {//if is required, because the last element of $parts might not end with a number
        $percentages[] = $match[0];
    }
}

Here, I simply use the % as delimiter, to create an array, and trim each string section (to avoid trailing whitespace), and then procede to check each substring, and match any number that is on the end of that substring:

'get 15% discount'
['get 15', 'discount']
/\d+$/, 'get 15' = [15]

But that's just an awful lot of work, using a lookahead is just way easier.

Upvotes: 4

anubhava
anubhava

Reputation: 785481

This should work:

$str = "Save 20% on iPhone chargers...";
if (preg_match_all('/\d+(?=%)/', $str, $match))
   print_r($match[0]);

Live Demo: http://ideone.com/FLKtE9

Upvotes: 0

웃웃웃웃웃
웃웃웃웃웃

Reputation: 11984

$str = "Half Board, 10% Off & 100 Euro Star - Save £535";
preg_match("|\d+|", $str, $arr);
print_r($arr);

Upvotes: 1

GautamD31
GautamD31

Reputation: 28753

Try with split like

$str_arr = split(' ',$str);
$my_str = split('%',$str_arr[1]);
echo $my_str[0];

Upvotes: 0

Related Questions