Header Ads Widget

Ticker

6/recent/ticker-posts

Cómo verificar si una cadena contiene una subcadena en Python: en, índice y más

 Un concepto que me dejó en un bucle cuando tomé Python por primera vez fue verificar si una cadena contiene una subcadena. Después de todo, en mi primer idioma, Java, la tarea consistía en llamar a un método como indexOf()contains()Afortunadamente, Python tiene una sintaxis aún más limpia, y lo cubriremos hoy.

Para resumir, podemos verificar si una cadena contiene una subcadena usando la inpalabra clave. Por ejemplo, "Hi" in "Hi, John"devuelve verdadero. Dicho esto, hay varias otras formas de resolver este problema, incluido el uso de métodos como index()find()Consulte el resto del artículo para obtener más detalles.

Descripción del problema

Un problema común en la programación es detectar si una cadena es una subcadena de otra cadena. Por ejemplo, podríamos tener una lista de direcciones almacenadas como cadenas y queremos encontrar todas las direcciones en una calle determinada (por ejemplo, Elm Street):

1
2
3
4
5
6
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"

En ese caso, podríamos comprobar qué direcciones contienen el nombre de la calle (por ejemplo, 123 Elm Street). ¿Cómo hacemos algo como esto en Python?

En la mayoría de los lenguajes de programación, suele haber algún método de subcadena. Por ejemplo, en Java, las cadenas tienen un indexOfmétodo que devuelve un número positivo si se encontró la subcadena.

Incluso sin un método especial, la mayoría de los lenguajes le permiten indexar cadenas como matrices. Como resultado, es posible verificar manualmente que una cadena contiene una subcadena buscando una coincidencia directamente.

En la siguiente sección, veremos varias posibles soluciones en Python.

Soluciones

Como siempre, me gusta compartir algunas posibles soluciones a este problema. Dicho esto, si quieres la mejor solución, te sugiero que saltes a la última solución.

Comprobación de si la cadena contiene subcadena por fuerza bruta

Siempre que trato de resolver un problema como este, me gusta pensar en la estructura subyacente del problema. En este caso, tenemos una cadena que en realidad es una lista de caracteres. Como resultado, lo que nos impide iterar sobre esos caracteres para encontrar nuestra subcadena:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
 
for address in addresses:
    address_length = len(address)
    street_length = len(street)
    for index in range(address_length - street_length + 1):
        substring = address[index:street_length + index]
        if substring == street:
            print(address)

Aquí, he escrito una especie de desagradable conjunto de bucles que iteran sobre todas las direcciones, calculan las longitudes de algunas cadenas, iteran sobre todas las subcadenas del tamaño apropiado e imprimen los resultados si se encuentra una subcadena adecuada.

Afortunadamente, no tenemos que escribir nuestra propia solución para esto. De hecho, todo el bucle interno ya está implementado como parte de cadenas. En la siguiente sección, veremos uno de esos métodos.

Comprobando si la cadena contiene subcadena usando index()

Si queremos comprobar si una cadena contiene una subcadena en Python, podríamos intentar tomar prestado algún código de un lenguaje como Java. Como se mencionó anteriormente, usualmente usamos el indexOf()método que devuelve un índice de la subcadena. En Python, hay un método similar llamado index():

01
02
03
04
05
06
07
08
09
10
11
12
13
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
 
for address in addresses:
    try:
        address.index(street)
        print(address)
    except ValueError:
        pass

Aquí, llamamos a la función de índice sin almacenar el resultado. Después de todo, en realidad no nos importa cuál es el índice. Si el método no encuentra una subcadena coincidente, lanzará una excepción. Naturalmente, podemos detectar esa excepción y seguir adelante. De lo contrario, imprimimos la dirección.

Si bien esta solución hace el trabajo, en realidad hay una solución un poco más limpia, y la veremos en la siguiente sección.

Comprobando si la cadena contiene subcadena usando find()

Curiosamente, Python tiene otro método similar al index()que funciona de manera casi idéntica a indexOf()Java. Se llama find()y nos permite simplificar un poco nuestro código:

01
02
03
04
05
06
07
08
09
10
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
 
for address in addresses:
    if address.find(street) > 0:
        print(address)

Ahora, esa es una solución que puedo respaldar. Después de todo, recuerda bastante a una solución Java similar.

Nuevamente, funciona como index()Sin embargo, en lugar de lanzar una excepción si la subcadena no existe, devuelve -1. Como resultado, podemos reducir nuestro bloque try / except a una sola instrucción if.

Dicho esto, Python tiene una solución aún mejor que veremos en la siguiente sección.

Comprobación de si la cadena contiene una subcadena mediante una inpalabra clave

Una de las cosas interesantes de Python es lo limpio y legible que puede ser el código. Naturalmente, esto se aplica cuando se comprueba si una cadena contiene una subcadena. En lugar de un método elegante, Python tiene la sintaxis incorporada con la inpalabra clave:

01
02
03
04
05
06
07
08
09
10
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
 
for address in addresses:
    if street in address:
        print(address)

Aquí, usamos la inpalabra clave dos veces: una vez para iterar sobre todas las direcciones en la lista de direcciones y nuevamente para verificar si la dirección contiene el nombre de la calle. Como puede ver, la inpalabra clave tiene dos propósitos:

  • Para verificar si un valor está presente en una secuencia como listas y cadenas
  • Para iterar a través de una secuencia

Por supuesto, para alguien que provenga de un lenguaje como Java, esta puede ser una respuesta bastante molesta. Después de todo, nuestra intuición es utilizar un método aquí, por lo que es necesario acostumbrarse. Dicho esto, me gusta mucho cómo se lee esto. Como veremos más adelante, esta también es la solución más rápida.

Actuación

Con todas estas soluciones listas para usar, echemos un vistazo a cómo se comparan. Para comenzar, necesitaremos configurar las soluciones en cadenas:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
setup = """
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
"""
 
brute_force = """
for address in addresses:
    address_length = len(address)
    street_length = len(street)
    for index in range(address_length - street_length + 1):
        substring = address[index:street_length + index]
        if substring == street:
            pass # I don't want to print during testing
"""
 
index_of = """
for address in addresses:
    try:
        address.index(street)
        # Again, I don't actually want to print during testing
    except ValueError:
        pass
"""
 
find = """
for address in addresses:
    if address.find(street) > 0:
        pass # Likewise, nothing to see here
"""
 
in_keyword = """
for address in addresses:
    if street in address:
        pass # Same issue as above
"""

Con estas cadenas listas para funcionar, podemos comenzar a probar:

1
2
3
4
5
6
7
8
9
>>>> import timeit
>>> min(timeit.repeat(setup=setup, stmt=brute_force))
4.427290499999998
>>> min(timeit.repeat(setup=setup, stmt=index_of))
1.293616
>>> min(timeit.repeat(setup=setup, stmt=find))
0.693925500000006
>>> min(timeit.repeat(setup=setup, stmt=in_keyword))
0.2180926999999997

¡Ahora, esos son algunos resultados convincentes! Resulta que la fuerza bruta es bastante lenta. Además, parece que el manejo de errores de la index()solución no es mucho mejor. Afortunadamente, find()existe para eliminar algunos de esos gastos generales. Dicho esto, ines la solución más rápida con diferencia.

Como suele ser el caso en Python, obtendrá el mejor rendimiento de los modismos comunes. En este caso, no intente escribir su propio método de subcadena. En su lugar, utilice la inpalabra clave incorporada .

Desafío

Ahora que sabe cómo verificar si una cadena contiene una subcadena, hablemos del desafío. Vamos a escribir un motor de búsqueda de direcciones simple que filtra por dos palabras clave en lugar de una: calle y número. Sin embargo, es posible que no obtengamos ambos datos en el momento de la búsqueda. Como resultado, debemos ocuparnos de encontrar direcciones que coincidan exactamente con las palabras clave disponibles.

Para este desafío, puede escribir cualquier solución que desee siempre que imprima una lista de direcciones que coincida exactamente con los términos de búsqueda. Por ejemplo, tome la siguiente lista de direcciones:

1
2
3
4
5
addresses = [
    "123 Elm Street",
    "123 Oak Street",
    "678 Elm Street"
]

Si un usuario busca simplemente "Elm Street", entonces esperaría que la solución devuelva "123 Elm Street" y "678 Elm Street". Del mismo modo, si un usuario busca "123", esperaría que la solución devuelva "123 Elm Street" y "123 Oak Street". Sin embargo, si el usuario proporciona "123" y "Elm Street", esperaría que la solución solo devuelva "123 Elm Street", no las tres direcciones.

Siéntete libre de divertirte con esto. Por ejemplo, puede optar por escribir una interfaz completa para recopilar las palabras clave de calle y número, o puede asumir que ambas variables ya existen.

En términos de datos de entrada, siéntase libre de escribir su propia lista de direcciones o use mi ejemplo simple. Alternativamente, puede utilizar un sitio web que genere direcciones aleatorias .

En última instancia, el programa debe demostrar el filtrado en dos palabras clave. En otras palabras, encuentre una manera de modificar una de las soluciones de este artículo para que coincida con la calle, la dirección o ambas, según lo que esté disponible en el momento de la ejecución.

En los comentarios a continuación, compartiré mi solución. ¡Siéntete libre de hacer lo mismo!

Un pequeño resumen

Y con eso, terminamos. Como resumen final, aquí están todas las soluciones que vio hoy:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
dieciséis
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
addresses = [
    "123 Elm Street",
    "531 Oak Street",
    "678 Maple Street"
]
street = "Elm Street"
 
# Brute force (don't do this)
for address in addresses:
    address_length = len(address)
    street_length = len(street)
    for index in range(address_length - street_length + 1):
        substring = address[index:street_length + index]
        if substring == street:
            print(address)
 
# The index method
for address in addresses:
    try:
        address.index(street)
        print(address)
    except ValueError:
        pass
 
# The find method
for address in addresses:
    if address.find(street) > 0:
        print(address)
 
# The in keyword (fastest/preferred)
for address in addresses:
    if street in address:
        print(address)

Publicar un comentario

0 Comentarios