222

Is there an efficient way to identify the last character/string match in a string using base functions? I.e. not the last character/string of the string, but the position of a character/string's last occurrence in a string. Search and find both work left-to-right so I can't think how to apply without lengthy recursive algorithm. And this solution now seems obsolete.

JvdV
  • 70,606
  • 8
  • 39
  • 70
geotheory
  • 22,624
  • 29
  • 119
  • 196
  • 3
    Because I want position of the last instance of e.g. period "." in the string "one.two.three.four" – geotheory Sep 04 '13 at 15:08
  • 6
    Amusing how an erroneous reading of the question garners up-votes – geotheory Sep 04 '13 at 17:07
  • 1
    I'd say look at it from the other perspective, it means other people also didn't understand what you meant in your question either and so figured the suggestion was the correct solution... Even your selected answer started with "I think I get what you mean"... Not a criticism, but rather a request to aim to make your questions more easy to understand to help people answer more easily. – John Bustos Sep 04 '13 at 17:20
  • I should've added the example to the question, but I think there was enough to distinguish this from a query about the final character of a string: `search` and `find` both query string _content_, 'match' is a standard term, plus the linked example. – geotheory Sep 04 '13 at 19:24
  • possible duplicate of [How to extract the last substring from a Excel column?](http://stackoverflow.com/questions/6133287/how-to-extract-the-last-substring-from-a-excel-column) – Jean-François Corbett Aug 15 '14 at 08:01
  • (Slightly newer) cross-site duplicate: https://superuser.com/q/680769 – Mathieu K. Mar 14 '20 at 19:24

14 Answers14

376

I think I get what you mean. Let's say for example you want the right-most \ in the following string (which is stored in cell A1):

Drive:\Folder\SubFolder\Filename.ext

To get the position of the last \, you would use this formula:

=FIND("@",SUBSTITUTE(A1,"\","@",(LEN(A1)-LEN(SUBSTITUTE(A1,"\","")))/LEN("\")))

That tells us the right-most \ is at character 24. It does this by looking for "@" and substituting the very last "\" with an "@". It determines the last one by using:

(len(string)-len(substitute(string, substring, "")))/len(substring)

In this scenario, the substring is simply "\" which has a length of 1, so you could leave off the division at the end and just use:

=FIND("@",SUBSTITUTE(A1,"\","@",LEN(A1)-LEN(SUBSTITUTE(A1,"\",""))))

Now we can use that to get the folder path:

=LEFT(A1,FIND("@",SUBSTITUTE(A1,"\","@",LEN(A1)-LEN(SUBSTITUTE(A1,"\","")))))

Here's the folder path without the trailing \:

=LEFT(A1,FIND("@",SUBSTITUTE(A1,"\","@",LEN(A1)-LEN(SUBSTITUTE(A1,"\",""))))-1)

And to get just the filename:

=MID(A1,FIND("@",SUBSTITUTE(A1,"\","@",LEN(A1)-LEN(SUBSTITUTE(A1,"\",""))))+1,LEN(A1))

However, if you can assure your string in A1 does not contain repeated spaces between words†, here is an alternate version of getting everything to the right of the last instance of a specific character. So using our same example, this would also return the file name:

=TRIM(RIGHT(SUBSTITUTE(A1,"\",REPT(" ",LEN(A1))),LEN(A1)))

† because this is what the TRIM documentation states (emphasis mine):

Removes all spaces from text except for single spaces between words. Use TRIM on text that you have received from another application that may have irregular spacing.

Since filenames can potentially contain repeating spaces, using the TRIM based solution can give incorrect results with repeating spaces replaced by single spaces.

Jeroen Wiert Pluimers
  • 23,965
  • 9
  • 74
  • 154
tigeravatar
  • 26,199
  • 5
  • 30
  • 38
  • 27
    head spinning; head spinnning; head spinning; "ok - don't wanna use this without understanding it, so... uh huh, ok, uh huh... wait what? whoah! really? that's genius! I feel confident I would not have thought of that in several hours if not days or weeks!" - +1, sadly, because +5 or +10 is hard to do on my own --- if-you're-stuck-explanation: (using the example) `SUBSTITUTE` all (3) instances of ``\`` with nothing (shorten length of string by 3) -> that ``\`` (the 3rd) is the last one; replace that with something unique and `FIND` the position of that unique character... awesome... thanks! – Code Jockey Feb 19 '14 at 20:56
  • 8
    It is clever. Just need to beware that the cell doesn't already contain '@' otherwise you'll need to sub with something else. You can check using `=ISNUMBER(SEARCH("@",A1))`, [as proposed by @gwin003](http://stackoverflow.com/questions/18617175/excel-check-if-a-cell-contains-a-substring). – geotheory Feb 27 '14 at 12:13
  • 4
    Your last option for extracting everything to the right of the last occurence is much appreciated because most of the time I've been looking for the "last occurence of string x within string y", my end goal has really been to get everything to the right of that last occurence. – SSilk Jul 23 '14 at 16:34
  • 1
    Why `99`? Are you just assuming the length of these various strings are less than 99? See [this answer](http://stackoverflow.com/a/5505006/119775) and [this answer](http://stackoverflow.com/questions/6133287/how-to-extract-the-last-substring-from-a-excel-column/6135343#6135343) which make no such assumption. – Jean-François Corbett Aug 15 '14 at 08:03
  • 4
    Instead of "@", I used [CHAR(9)](http://superuser.com/a/402326) (the tab character), since my data originally comes from tab-delimited file, and I'm guaranteed it won't be in my data. – Josiah Yoder Jun 16 '15 at 21:11
  • Maybe I missed some of the fine points, but I think the solution below is completely generic: RIGHT(A1,LEN(A1)-FIND("@",SUBSTITUTE(A1,CHAR(32),"@",(LEN(A1)-LEN(SUBSTITUTE(A1,CHAR(32),"")))))), where CHAR(32) evaluates unambiguously to a single space character. – David A. Gray Nov 21 '17 at 05:43
  • 1
    Just came across one circumstance where the last option won't correctly return the file name. Excel's TRIM also removes repeating spaces in the middle of text, so if your filename contains two or more consecutive spaces in the middle of it, these will be replaced with a single space. The =MID(A1 ... version works OK though. – Mick O'Hea Jan 10 '18 at 14:52
  • works perfect, but only @tigeravatar and god may knows the logic ;-) – vels4j Apr 10 '18 at 13:08
  • That last line - should be an answer on its own, its perfect. Logic wise took a while to unravel but here goes: (1) Take original string length. (2) Replace all "\" with whitespace strings as long as the original string. (3) Take the right most original-length-of-string characters, which will be a bunch of white space plus file name now. (4) Trim off white space. – mccdyl001 Jan 16 '19 at 11:33
  • 2
    For a different explanation of the same answer: https://superuser.com/a/680776 – Mathieu K. Mar 14 '20 at 18:30
  • @mccdyl001 : Mick O'Hea is right. Though the `TRIM` function based solution is shorter than the other one, `TRIM` will replace repeating spaces with single spaces. I updated the answer to emphasise that (and link to the documentation) as it is a risk you need to be aware of when using `TRIM`. – Jeroen Wiert Pluimers Mar 22 '23 at 21:54
33

How about creating a custom function and using that in your formula? VBA has a built-in function, InStrRev, that does exactly what you're looking for.

Put this in a new module:

Function RSearch(str As String, find As String)
    RSearch = InStrRev(str, find)
End Function

And your function will look like this (assuming the original string is in B1):

=LEFT(B1,RSearch(B1,"\"))
Patrick Hofman
  • 153,850
  • 22
  • 249
  • 325
Keith Rust
  • 431
  • 4
  • 3
23

New Answer | 31-3-2022:

With even newer functions come even shorter answers. At time of writing in BETA, but probably widely available in the near future, we can use TEXTBEFORE():

=LEN(TEXTBEFORE(A2,B2,-1))+1

The trick here is that the 3rd parameter tells the function to retrieve the last occurence of the substring we give in the 2nd parameter. At time of writing this function is still case-sensitive by default which could be handeld by the optional 4th parameter.


Original Answer | 17-6-2020:

With newer versions of excel come new functions and thus new methods. Though it's replicable in older versions (yet I have not seen it before), when one has Excel O365 one can use:

=MATCH(2,1/(MID(A1,SEQUENCE(LEN(A1)),1)="Y"))

This can also be used to retrieve the last position of (overlapping) substrings:

=MATCH(2,1/(MID(A1,SEQUENCE(LEN(A1)),2)="YY"))

| Value  | Pattern | Formula                                        | Position |
|--------|---------|------------------------------------------------|----------|
| XYYZ   | Y       | =MATCH(2,1/(MID(A2,SEQUENCE(LEN(A2)),1)="Y"))  | 3        |
| XYYYZ  | YY      | =MATCH(2,1/(MID(A3,SEQUENCE(LEN(A3)),2)="YY")) | 3        |
| XYYYYZ | YY      | =MATCH(2,1/(MID(A4,SEQUENCE(LEN(A4)),2)="YY")) | 4        |

Whilst this both allows us to no longer use an arbitrary replacement character and it allows overlapping patterns, the "downside" is the useage of an array.


Note: You can force the same behaviour in older Excel versions through either

=MATCH(2,1/(MID(A2,ROW(A1:INDEX(A:A,LEN(A2))),1)="Y"))

Entered through CtrlShiftEnter, or using an inline INDEX to get rid of implicit intersection:

=MATCH(2,INDEX(1/(MID(A2,ROW(A1:INDEX(A:A,LEN(A2))),1)="Y"),))
JvdV
  • 70,606
  • 8
  • 39
  • 70
  • 2
    I think this inherits the tick now, though full historical honours belong to @tigeravatar's longstanding solution https://stackoverflow.com/a/18617720/1156245 – geotheory Jun 18 '20 at 01:13
  • 1
    **Note:** Whilst my suggestion would win at sketchy code-golf, the usage of arrays is not always recommended when used in large quantities. It does however, has other benefits which I've tried to explicitly mention. This makes the answer by @Tigeravatar just as relevant as before! – JvdV Jun 18 '20 at 07:32
  • It worked. Thank you! – Steven Lee Dec 01 '21 at 02:24
9

tigeravatar and Jean-François Corbett suggested to use this formula to generate the string right of the last occurrence of the "\" character

=TRIM(RIGHT(SUBSTITUTE(A1,"\",REPT(" ",LEN(A1))),LEN(A1)))

If the character used as separator is space, " ", then the formula has to be changed to:

=SUBSTITUTE(RIGHT(SUBSTITUTE(A1," ",REPT("{",LEN(A1))),LEN(A1)),"{","")

No need to mention, the "{" character can be replaced with any character that would not "normally" occur in the text to process.

BigBen
  • 46,229
  • 7
  • 24
  • 40
  • I find this solution way more elegant and understandable. Also if you multiply LEN(A1) by a number you can get nth to last occurrence, provided your original string does not contain spaces (which was the case for me) – Zlatin Zlatev Apr 13 '20 at 10:56
6

Just came up with this solution, no VBA needed;

Find the last occurance of "_" in my example;

=IFERROR(FIND(CHAR(1);SUBSTITUTE(A1;"_";CHAR(1);LEN(A1)-LEN(SUBSTITUTE(A1;"_";"")));0)

Explained inside out;

SUBSTITUTE(A1;"_";"") => replace "_" by spaces
LEN( *above* ) => count the chars
LEN(A1)- *above*  => indicates amount of chars replaced (= occurrences of "_")
SUBSTITUTE(A1;"_";CHAR(1); *above* ) => replace the Nth occurence of "_" by CHAR(1) (Nth = amount of chars replaced = the last one)
FIND(CHAR(1); *above* ) => Find the CHAR(1), being the last (replaced) occurance of "_" in our case
IFERROR( *above* ;"0") => in case no chars were found, return "0"

Hope this was helpful.

phuclv
  • 37,963
  • 15
  • 156
  • 475
Mr Thee
  • 61
  • 1
  • 1
  • This works, but you were missing a ) before the ";0)" =IFERROR(FIND(CHAR(1),SUBSTITUTE(A1,"/",CHAR(1),LEN(A1)-LEN(SUBSTITUTE(A1,"/","")))),0) – Lodlaiden Jan 20 '23 at 10:37
4

Considering a part of a Comment made by @SSilk my end goal has really been to get everything to the right of that last occurence an alternative approach with a very simple formula is to copy a column (say A) of strings and on the copy (say ColumnB) apply Find and Replace. For instance taking the example: Drive:\Folder\SubFolder\Filename.ext

Find what

This returns what remains (here Filename.ext) after the last instance of whatever character is chosen (here \) which is sometimes the objective anyway and facilitates finding the position of the last such character with a short formula such as:

=FIND(B1,A1)-1
pnuts
  • 58,317
  • 11
  • 87
  • 139
3

You could use this function I created to find the last instance of a string within a string.

Sure the accepted Excel formula works, but it's much too difficult to read and use. At some point you have to break out into smaller chunks so it's maintainable. My function below is readable, but that's irrelevant because you call it in a formula using named parameters. This makes using it simple.

Public Function FindLastCharOccurence(fromText As String, searchChar As String) As Integer
Dim lastOccur As Integer
lastOccur = -1
Dim i As Integer
i = 0
For i = Len(fromText) To 1 Step -1
    If Mid(fromText, i, 1) = searchChar Then
        lastOccur = i
        Exit For
    End If
Next i

FindLastCharOccurence = lastOccur
End Function

I use it like this:

=RIGHT(A2, LEN(A2) - FindLastCharOccurence(A2, "\"))
Michael Z.
  • 1,453
  • 1
  • 15
  • 21
1

I'm a little late to the party, but maybe this could help. The link in the question had a similar formula, but mine uses the IF() statement to get rid of errors.

If you're not afraid of Ctrl+Shift+Enter, you can do pretty well with an array formula.

String (in cell A1): "one.two.three.four"

Formula:

{=MAX(IF(MID(A1,ROW($1:$99),1)=".",ROW($1:$99)))}  use Ctrl+Shift+Enter

Result: 14

First,

ROW($1:$99)

returns an array of integers from 1 to 99: {1,2,3,4,...,98,99}.

Next,

MID(A1,ROW($1:$99),1)

returns an array of 1-length strings found in the target string, then returns blank strings after the length of the target string is reached: {"o","n","e",".",..."u","r","","",""...}

Next,

IF(MID(I16,ROW($1:$99),1)=".",ROW($1:$99))

compares each item in the array to the string "." and returns either the index of the character in the string or FALSE: {FALSE,FALSE,FALSE,4,FALSE,FALSE,FALSE,8,FALSE,FALSE,FALSE,FALSE,FALSE,14,FALSE,FALSE.....}

Last,

=MAX(IF(MID(I16,ROW($1:$99),1)=".",ROW($1:$99)))

returns the maximum value of the array: 14

Advantages of this formula is that it is short, relatively easy to understand, and doesn't require any unique characters.

Disadvantages are the required use of Ctrl+Shift+Enter and the limitation on string length. This can be worked around with a variation shown below, but that variation uses the OFFSET() function which is a volatile (read: slow) function.

Not sure what the speed of this formula is vs. others.

Variations:

=MAX((MID(A1,ROW(OFFSET($A$1,,,LEN(A1))),1)=".")*ROW(OFFSET($A$1,,,LEN(A1)))) works the same way, but you don't have to worry about the length of the string

=SMALL(IF(MID(A1,ROW($1:$99),1)=".",ROW($1:$99)),2) determines the 2nd occurrence of the match

=LARGE(IF(MID(A1,ROW($1:$99),1)=".",ROW($1:$99)),2) determines the 2nd-to-last occurrence of the match

=MAX(IF(MID(I16,ROW($1:$99),2)=".t",ROW($1:$99))) matches a 2-character string **Make sure you change the last argument of the MID() function to the number of characters in the string you wish to match!
Stadem
  • 423
  • 1
  • 6
  • 15
1

In newer versions of Excel (2013 and up) flash fill might be a simple and quick solution see: Using Flash Fill in Excel .

RS Finance
  • 224
  • 1
  • 9
1

For a string in A1 and substring in B1, use:

=XMATCH(B1,MID(A1,SEQUENCE(LEN(A1)),LEN(B1)),,-1)

Working from inside out, MID(A1,SEQUENCE(LEN(A1)),LEN(B1)) splits string A1 into a dynamic array of substrings, each the length of B1. To find the position of the last occurrence of substring B1, we use XMATCH with its Search_mode argument set to -1.

bkraines
  • 83
  • 1
  • 4
0

A simple way to do that in VBA is:

YourText = "c:\excel\text.txt"
xString = Mid(YourText, 2 + Len(YourText) - InStr(StrReverse(YourText), "\" ))
0

Very late to the party, but A simple solution is using VBA to create a custom function.

Add the function to VBA in the WorkBook, Worksheet, or a VBA Module

Function LastSegment(S, C)
    LastSegment = Right(S, Len(S) - InStrRev(S, C))
End Function

Then the cell formula

=lastsegment(B1,"/")

in a cell and the string to be searched in cell B1 will populate the cell with the text trailing the last "/" from cell B1. No length limit, no obscure formulas. Only downside I can think is the need for a macro-enabled workbook.

Any user VBA Function can be called this way to return a value to a cell formula, including as a parameter to a builtin Excel function.

If you are going to use the function heavily you'll want to check for the case when the character is not in the string, then string is blank, etc.

Patrick
  • 5,526
  • 14
  • 64
  • 101
-1

If you're only looking for the position of the last instance of character "~" then =len(substitute(String,"~",""))+1

I'm sure there is version that will work with the last instance of a string but I have to get back to work.

Chris
  • 1
-2

Cell A1 = find/the/position/of/the last slash

simple way to do it is reverse the text and then find the first slash as normal. Now you can get the length of the full text minus this number.

Like so:

=LEN(A1)-FIND("/",REVERSETEXT(A1),1)+1

This returns 21, the position of the last /

Devang Padhiyar
  • 3,427
  • 2
  • 22
  • 42