Excel: última coincidencia de caracteres / cadenas en una cadena

187

¿Hay una manera eficiente de identificar la última coincidencia de caracteres / cadenas en una cadena utilizando funciones base? Es decir, no el último carácter / cadena de la cadena, sino la posición de la última aparición de un carácter / cadena en una cadena. Searchy findambos funcionan de izquierda a derecha, así que no puedo pensar cómo aplicar sin un largo algoritmo recursivo. Y esta solución ahora parece obsoleta.

geoteoria
fuente
3
Porque quiero posición de la última instancia de, por ejemplo, punto "." en la cadena "one.two.three.four"
geotheory
66
Divertido cómo una lectura errónea de la pregunta
genera
1
Diría que lo mires desde la otra perspectiva, significa que otras personas tampoco entendieron lo que querías decir en tu pregunta y pensaron que la sugerencia era la solución correcta ... Incluso tu respuesta seleccionada comenzó con "Creo que yo entienda lo que quiere decir "... No es una crítica, sino más bien una solicitud para que sus preguntas sean más fáciles de entender y ayudar a las personas a responder más fácilmente.
John Bustos
Debería haber agregado el ejemplo a la pregunta, pero creo que hubo suficiente para distinguir esto de una consulta sobre el carácter final de una cadena: searchy findtanto el contenido de la cadena de consulta , 'coincidencia' es un término estándar, más el ejemplo vinculado.
geotheory
posible duplicado de ¿Cómo extraer la última subcadena de una columna de Excel?
Jean-François Corbett

Respuestas:

1

Con las versiones más nuevas de Excel vienen nuevas funciones y, por lo tanto, nuevos métodos. Aunque es replicable en versiones anteriores (aún no lo he visto antes), cuando uno tiene Excel O365 se puede usar:

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

Esto también se puede utilizar para recuperar la última posición de las subcadenas (superpuestas):

=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        |

Si bien esto nos permite dejar de usar un carácter de reemplazo arbitrario y permite patrones superpuestos, el "inconveniente" es el uso de una matriz.


Nota: Puede forzar el mismo comportamiento en versiones anteriores de Excel a través de cualquiera

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

Ingresado a través de CtrlShiftEnter, o usando una línea INDEXpara deshacerse de la intersección implícita:

=MATCH(2,INDEX(1/(MID(A2,ROW(A1:INDEX(A:A,LEN(A2))),1)="Y"),))
JvdV
fuente
1
Creo que esto hereda el tic ahora, aunque los honores históricos completos pertenecen a la solución de larga data de @ tigeravatar stackoverflow.com/a/18617720/1156245
geotheory
Nota: Si bien mi sugerencia ganaría en el código incompleto de golf, el uso de matrices no siempre se recomienda cuando se usa en grandes cantidades. Sin embargo, tiene otros beneficios que he tratado de mencionar explícitamente. ¡Esto hace que la respuesta de @Tigeravatar sea tan relevante como antes!
JvdV
345

Creo que entiendo lo que quieres decir. Digamos, por ejemplo, que desea el extremo derecho \ en la siguiente cadena (que se almacena en la celda A1):

Unidad: \ Carpeta \ Subcarpeta \ Nombre de archivo.ext

Para obtener la posición de la última \, usaría esta fórmula:

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

Eso nos dice que la más a la derecha está en el carácter 24. Lo hace buscando "@" y sustituyendo la última "\" con una "@". Determina el último usando

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

En este escenario, la subcadena es simplemente "\" que tiene una longitud de 1, por lo que puede dejar la división al final y simplemente usar:

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

Ahora podemos usar eso para obtener la ruta de la carpeta:

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

Aquí está la ruta de la carpeta sin el final \

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

Y para obtener solo el nombre del archivo:

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

Sin embargo, aquí hay una versión alternativa de obtener todo a la derecha de la última instancia de un personaje específico. Entonces, usando nuestro mismo ejemplo, esto también devolvería el nombre del archivo:

=TRIM(RIGHT(SUBSTITUTE(A1,"\",REPT(" ",LEN(A1))),LEN(A1)))
tigeravatar
fuente
24
cabeza dando vueltas; cabeza dando vueltas; cabeza dando vueltas; "ok, no quiero usar esto sin entenderlo, así que ... uh huh, ok, uh huh ... espera, ¿qué? si no días o semanas! " - +1, lamentablemente, porque +5 o +10 es difícil de hacer por mi cuenta --- si estás atascado-explicación: (usando el ejemplo) SUBSTITUTEtodas (3) instancias \sin nada (acortar la longitud de la cadena por 3) -> que \(el 3er) es el último; reemplace eso con algo único y FINDla posición de ese personaje único ... increíble ... ¡gracias!
Code Jockey
77
Es inteligente Solo debe tener cuidado de que la celda no contenga '@', de lo contrario, deberá sustituir con otra cosa. Puede verificar usando =ISNUMBER(SEARCH("@",A1)), según lo propuesto por @ gwin003 .
geotheory
3
Su última opción para extraer todo a la derecha de la última ocurrencia es muy apreciada porque la mayoría de las veces he estado buscando la "última ocurrencia de la cadena x dentro de la cadena y", mi objetivo final ha sido realmente llevar todo a la derecho de esa última ocurrencia.
SSilk
1
¿Por qué 99? ¿Estás asumiendo que la longitud de estas diferentes cadenas es menor que 99? Vea esta respuesta y esta respuesta que no hacen tal suposición.
Jean-François Corbett
2
En lugar de "@", utilicé CHAR (9) (el carácter de tabulación), ya que mis datos provienen originalmente de un archivo delimitado por tabuladores, y estoy seguro de que no estarán en mis datos.
Josiah Yoder
28

¿Qué tal crear una función personalizada y usarla en tu fórmula? VBA tiene una función incorporada InStrRevque hace exactamente lo que estás buscando.

Pon esto en un nuevo módulo:

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

Y su función se verá así (suponiendo que la cadena original esté en B1):

=LEFT(B1,RSearch(B1,"\"))
Keith Rust
fuente
2
Esta es una solución muy simple pero funcional. Si puede usar algo de VBA en su proyecto, use este.
Patrick Hofman
7

tigeravatar y Jean-François Corbett sugirieron usar esta fórmula para generar la cadena a la derecha de la última aparición del carácter "\"

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

Si el carácter utilizado como separador es espacio, "", entonces la fórmula debe cambiarse a:

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

No es necesario mencionar que el carácter "{" se puede reemplazar con cualquier carácter que no se produzca "normalmente" en el texto a procesar.

Eugen Sumindan
fuente
Encuentro esta solución mucho más elegante y comprensible. Además, si multiplica LEN (A1) por un número, puede obtener el enésimo hasta el último evento, siempre que su cadena original no contenga espacios (que fue el caso para mí)
Zlatin Zlatev
4

Se me ocurrió esta solución, no se necesita VBA;

Encuentre la última aparición de "_" en mi ejemplo;

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

Explicado de adentro hacia afuera;

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"

Espero que esto haya sido útil.

Señor te
fuente
3

Podrías usar esta función que creé para encontrar la última instancia de una cadena dentro de una cadena.

Claro que la fórmula de Excel aceptada funciona, pero es demasiado difícil de leer y usar. En algún momento, debe dividirse en trozos más pequeños para que sea mantenible. Mi función a continuación es legible, pero eso es irrelevante porque lo llamas en una fórmula que usa parámetros con nombre. Esto hace que usarlo sea 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

Lo uso así:

=RIGHT(A2, LEN(A2) - FindLastCharOccurence(A2, "\"))
Michael Z.
fuente
2

Teniendo en cuenta una parte de un comentario hecho por @SSilk, mi objetivo final ha sido realmente poner todo a la derecha de esa última ocurrencia. Un enfoque alternativo con una fórmula muy simple es copiar una columna (digamos A) de cadenas y en la copia (digamos ColumnaB) aplique Buscar y reemplazar. Por ejemplo tomando el ejemplo:Drive:\Folder\SubFolder\Filename.ext

Encontrar que

Esto devuelve lo que queda (aquí Filename.ext) después de la última instancia de cualquier personaje elegido (aquí \), que a veces es el objetivo de todos modos y facilita encontrar la posición del último personaje con una fórmula corta como:

=FIND(B1,A1)-1
nueces
fuente
1

Llego un poco tarde a la fiesta, pero tal vez esto pueda ayudar. El enlace en la pregunta tenía una fórmula similar, pero la mía usa la declaración IF () para deshacerse de los errores.

Si no tienes miedo de Ctrl + Shift + Enter, puedes hacerlo bastante bien con una fórmula de matriz.

Cadena (en la celda A1): "one.two.three.four"

Fórmula:

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

Resultado: 14

Primero,

ROW($1:$99)

devuelve una matriz de enteros de 1 a 99: {1,2,3,4,...,98,99}.

Próximo,

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

devuelve una matriz de cadenas de 1 longitud encontradas en la cadena de destino, luego devuelve cadenas en blanco después de alcanzar la longitud de la cadena de destino: {"o","n","e",".",..."u","r","","",""...}

Próximo,

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

compara cada elemento de la matriz con la cadena "." y devuelve el índice del carácter en la cadena o FALSO:{FALSE,FALSE,FALSE,4,FALSE,FALSE,FALSE,8,FALSE,FALSE,FALSE,FALSE,FALSE,14,FALSE,FALSE.....}

Último,

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

devuelve el valor máximo de la matriz: 14

La ventaja de esta fórmula es que es corta, relativamente fácil de entender y no requiere caracteres únicos.

Las desventajas son el uso requerido de Ctrl + Shift + Enter y la limitación en la longitud de la cadena. Esto se puede solucionar con una variación que se muestra a continuación, pero esa variación utiliza la función OFFSET (), que es una función volátil (lectura: lenta).

No estoy seguro de cuál es la velocidad de esta fórmula frente a otras.

Variaciones:

=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
fuente
1

En las versiones más recientes de Excel (2013 y posteriores), el relleno flash puede ser una solución simple y rápida; consulte: Uso de Flash Fill en Excel .

RS Finance
fuente
0

Una forma simple de hacerlo en VBA es:

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

Muy tarde para la fiesta, pero una solución simple es usar VBA para crear una función personalizada.

Agregue la función a VBA en el Libro de trabajo, Hoja de trabajo o un Módulo VBA

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

Entonces la fórmula celular

=lastsegment(B1,"/")

en una celda y la cadena que se buscará en la celda B1 llenará la celda con el texto detrás de la última "/" de la celda B1. Sin límite de longitud, sin fórmulas oscuras. El único inconveniente que puedo pensar es la necesidad de un libro de trabajo habilitado para macros.

Se puede llamar a cualquier función VBA de usuario de esta manera para devolver un valor a una fórmula de celda, incluso como parámetro a una función incorporada de Excel.

Si va a utilizar mucho la función, querrá verificar el caso en que el carácter no está en la cadena, entonces la cadena está en blanco, etc.

usuario8512894
fuente
0

Célula A1 = find/the/position/of/the last slash

Una forma simple de hacerlo es invertir el texto y luego encontrar la primera barra como de costumbre. Ahora puede obtener la longitud del texto completo menos este número.

Al igual que:

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

Esto devuelve 21, la posición del último /

Ricardo
fuente
REVERSETEXTno es una fórmula estándar de Excel
d219