0

Let me first start by saying I don't understand Reg-Ex much so I'm hoping someone can help me.

I have a string such as "/1/3" or maybe "/1/34/2/6" and I am trying to get the last number after the final /

A friend sent me this formula but all it give me is the last /

preg_replace('~^/([0-9]+)(/.*)?$~', '$1', $cat['path']);

Where $cat[path] is the string I've got. How should I change this to give me the final number and not the slash /?

PeeHaa
  • 71,436
  • 58
  • 190
  • 262
Wheelz
  • 219
  • 1
  • 2
  • 9

4 Answers4

4

I would solve this without using regex.

$parts = explode('/', $input);
$latest_one = array_pop($parts);
Daniel M
  • 3,369
  • 20
  • 30
  • 1
    -1 Too much computation for grabbing one single character (IMO). – Mihai Stancu Aug 20 '12 at 19:50
  • Upvote that. And actually I think that regexp is probably even more computation-heavy than a simple tokenisation. Then again, maybe it allows for simpler error checking (if (preg_match(...))). – LSerni Aug 20 '12 at 19:56
  • 2
    @MihaiStancu: Really? Are you serious? Regex is far worse than that. +1 on that. – Madara's Ghost Aug 20 '12 at 19:58
  • 2
    @Truth I would've agreed that regex is far worse if he had used strrpos & substr because they are simple loops over a string. But creating an array of exploded elements means using memory allocation for each node in the array. The array is an ordered HashMap read up on the concept to get a grasp of the complexity of it. – Mihai Stancu Aug 20 '12 at 20:04
  • I agree that arrays are costly, but where else does the regexp code capture its output? Anyway, I tried timing the various solutions (command-line PHP on Linux) and the explode() is indeed faster. The basename() solution of @salathe is even quicker (about 50%). Yours and mine are both slower by an appreciable margin, which worsens when the input string grows more complex. – LSerni Aug 20 '12 at 20:29
  • It's not a hack if the string is a path, which my answer clearly is prefixed with. – salathe Aug 20 '12 at 20:31
  • It wouldn't be the first issue on which PHP has some kind of optimization in place. This [article](http://schlueters.de/blog/archives/125-Do-not-use-PHP-references.html) shows how using references with functions that expect a pass-by-value can incur huge speed-downs to the script. All because PHP optimized its core functions for pass-by-value. – Mihai Stancu Aug 20 '12 at 20:37
3

You want:

#/(\d+)$#

where # are the delimiters. It means,

/      a slash,
\d+    a sequence of one or more digits,
$      then the end.

The parentheses mean that you want the sequence to be captured.

The PHP code would be:

$string = "1/12/26";

if (preg_match('#/(\d+)$#', $string, $results))
{
    $number = $results[1]; // [0] holds the whole string
    print "The last number is $number.";
}
LSerni
  • 55,617
  • 10
  • 65
  • 107
  • 2
    Why do you care about the slash? – salathe Aug 20 '12 at 19:56
  • 2
    Well... habit. Sadly, I am used to being told "There will be this token, then this other token", and after some time - weeks, but sometime just hours - I will get something totally different. That's the reason for the if(), too. Performance-wise, mine is a bad habit. Occasionally I even get false negatives (suppose there is a single number - "123" and nothing else?). In my experience, the advantages outweigh the occasional problems. But +thanks for pointing that out, and to @Lindrian too for the link. – LSerni Aug 20 '12 at 20:02
  • OK, so I've got it the wrong way around. how do I get the first number between the first and second slashes? – Wheelz Aug 22 '12 at 12:47
  • In that case, you use #^/(\d+)#. – LSerni Aug 22 '12 at 13:41
3

Is $cat['path'] a filesystem or URL path? PHP already has functions to deal with those.

$cat['path'] = '/1/34/2/6';
var_dump(
    basename($cat['path']),
    pathinfo($cat['path'], PATHINFO_BASENAME)
);

Helpful information

salathe
  • 51,324
  • 12
  • 104
  • 132
  • I raise (is this the correct word?) with: strrev((int)strrev($cat['path'])). – LSerni Aug 20 '12 at 20:08
  • 1
    @lserni `$cat['path'] = '/10/20/30'; // :'(` – salathe Aug 20 '12 at 20:16
  • basename is indeed a cunning trick for this. The RegEx approach is more generic, but this should be the fastest way to do it (most likely it uses c-level substr). – Mihai Stancu Aug 20 '12 at 20:21
  • Yep, it really is a *cunning trick*!… PHP has lots of tools (*cunning tricks*) for oftentimes very specific jobs. If the `$cat['path']` is indeed a path, then use `basename()`; otherwise, best not to use a hammer to do a saw's work. – salathe Aug 20 '12 at 20:30
  • PHP gives me the most mixed feelings regarding the big tool-bag it has. That's because they were obviously designed for a number of work-flows but within a project you will always have to use a specific tool outside of the designed work-flow. So you try three-four different hammers to see which can do the saw's job best. – Mihai Stancu Aug 20 '12 at 20:34
  • @salathe when I have a saw I use a saw (obviously). I wouldn't recommend OP to use RegEx at all but i was answering his question. In lue of `basename` i would've used strpos & substr. But there are many weird inconsistencies in PHP and many places that were keenly guided towards a purpose - hence diverging means having to build your own saw... from hammers (sometimes). Like using Ordered MultiHashMaps ever-god-dam-where including the underlying implementations of classes and not having access to some simpler dynamic-vectors. – Mihai Stancu Aug 20 '12 at 20:50
-1
preg_match('{/(\d)[^\d]*$}', $string, $matches);
$matches[0]; // this should be your number
  • { and } are delimiters - usual delimiters are / and / or @ and @ but when it is convenient you choose the delimiters that allow you to escape as few characters as possible;
  • / matches one slash - if we would've used the / delimiters we would have needed to escape this slash with a backslash;
  • \d matches one digit - \d* would match any number of digits including no digits, \d+ would match any number of digits but at least one digit;
  • [^\d]* matches anything that is not a digit; this could be optional if you are sure the last character before the end of the string is never anything else except a digit;
  • $ matches the end of the string - this ensures us we are looking at towards the end of the string;

Equivalent example using / delimiters:

preg_match('/\/(\d)[^\d]*$/', $string, $matches);
Mihai Stancu
  • 15,848
  • 2
  • 33
  • 51
  • You are incorrect. First off, `$matches[0]` will contain the `/`. You want to use `$matches[1]`. Secondly, you only allow for 1 digit, what if there are several? I would change `\d` to `\d+`. – Firas Dib Aug 20 '12 at 19:52
  • @Lindrian first off matches would not contain the slash it would contain the first group which is the digit. Secondly yes OP's question lead me to believe he wanted the last **digit** not the last **number**. – Mihai Stancu Aug 20 '12 at 19:57
  • @salathe I respect you opinion, they are useful for me and I am accustomed to them. They are of less use to a rookie who should know slashes are the norm, but since i explain it in my text i think it's ok. – Mihai Stancu Aug 20 '12 at 19:58
  • 2
    If you want anyone reading your regex to make the ಠ_ಠ face, then feel free to keep the curly braces. Don't let me stop you! P.S. I'm all *for* **not** using slash as delimiter when the pattern contains slashes, but bracket/brace pairs… no thanks. – salathe Aug 20 '12 at 20:02
  • @MihaiStancu: No, you are wrong. _$matches[0]_ will contain the text that matched the full pattern. – Firas Dib Aug 20 '12 at 20:04
  • @salathe since it's a matter of taste it has no place in this debate. Everyone's entitled to an opinion. We can each have one. I combated a fellow SO user the same way not long ago. Since then I tried it and got used to it and am trying to spread the word about it. After all people can get used to new things and not make the ಠ_ಠ face if it becomes "the new norm" – Mihai Stancu Aug 20 '12 at 20:07
  • 1
    It is not a matter of taste, it is a matter of code legibility plain and simple: your frivolous use of the curly braces takes extra (wasted) time to digest than more common choices. – salathe Aug 20 '12 at 20:11
  • I agree with @salathe. Programming as all about lowering the number of WTF's per minute when somebody else is (re)viewing your code. And that is a WTF. – PeeHaa Aug 20 '12 at 20:14
  • @PeeHaa I know it's a WTF that's why I commented/described/offered the alternative etc.. Unfortunately I feel salathe is growing more annoyed with the justifications I offered than he was with the deed itself. Since I gave way where there was way to give IMO I would expect he would at least maintain his opinion without slamming his fist into the table. – Mihai Stancu Aug 20 '12 at 20:19
  • You haven't seen me slam my fist into anything yet, @MihaiStancu. You'll know it when it happens (though it won't ever happen on SO)! I am mostly intrigued and a little amused by your alien point of view. :) – salathe Aug 20 '12 at 20:41
  • It was a more of a figure of speech relating to an action of mine being described as frivolous even though I had justified my point. Even if you don't agree with my point the action can no longer be called frivolous since it has a justification however contorted it may seem - we're not all cut from the same cloth after all. – Mihai Stancu Aug 20 '12 at 20:45
  • Thanks for the clarification. I'm still happy with my word choice, however. Maybe you have a different definition or interpretation of it than I am using in this case (indeed, we're not all cut from the same cloth after all!). Since we're wandering away from commenting on the answers, lets call it a night. Agreed? :) – salathe Aug 20 '12 at 21:01