Ruby regex - LPC-Ltda/Ruby-on-Rails GitHub Wiki

Dominar las expresiones regulares de Ruby

Las expresiones regulares de Ruby (ruby regex para abreviar) lo ayudan a encontrar patrones específicos dentro de las cadenas, con la intención de extraer datos para su posterior procesamiento.

Dos casos de uso comunes para las expresiones regulares incluyen la validatión & parsing.

Por ejemplo:

Piense en una dirección de correo electrónico, con una expresión regular de ruby ​​puede definir cómo se ve una dirección de correo electrónico válida. En otras palabras, su programa podrá diferenciar entre una dirección de correo electrónico válida y una no válida.

Las expresiones regulares de Ruby se definen entre dos barras diagonales para diferenciarlas de la sintaxis de otros idiomas. Las expresiones más simples coinciden con una palabra o incluso una sola letra.

Por ejemplo:

    # Find the word 'like'
    "Do you like cats?" =~ /like/

Esto devuelve el índice de la primera aparición de la palabra si se encontró o nil en caso contrario. Si no nos importa el índice, podríamos usar el método String#include?.

Otra forma de verificar si una cadena coincide con una expresión regular es usar el método de match:

    if "Do you like cats?".match(/like/)
      puts "Match found!"
    end

Ahora aprenderá a crear patrones más avanzados para que pueda hacer coincidir, capturar y reemplazar cosas como fechas, números de teléfono, URL, etc.

Clases de Caracteres

Una clase de carácter le permite definir un rango o una lista de caracteres para que coincidan. Por ejemplo, [aeiou] coincide con cualquier vocal.

Por ejemplo: el string contiene una vocal?

    def contains_vowel(str)
      str =~ /[aeiou]/
    end
    contains_vowel("test") # returns 1
    contains_vowel("sky")  # returns nil

Esto no tendrá en cuenta la cantidad de caracteres, veremos cómo hacerlo pronto.

Rangos

Podemos usar rangos para hacer coincidir varias letras o números sin tener que escribirlos todos. En otras palabras, un rango como [2-5] es lo mismo que [2345].

Algunos rangos útiles:

  • [0-9] coincide con cualquier número del 0 al 9
  • [a-z] coincide con cualquier letra de la a a la z (minúsculas)
  • [^ a-z] rango negado

Ejemplo: ¿Este string contiene números?

    def contains_number(str)
      str =~ /[0-9]/
    end
    contains_number("The year is 2015")  # returns 12
    contains_number("The cat is black")  # returns nil

Recuerde: el valor de retorno cuando se usa = ~ es el índice del string o nil

Hay sintaxis abreviada para especificar rangos de caracteres:

  • \w es equivalente a [0-9a-zA-Z_]
  • \d es lo mismo que [0-9]
  • \s coincide con el espacio en blanco (tabulaciones, espacio regular, nueva línea)

También existe la forma negativa de estos:

  • \W cualquier cosa que no esté en [0-9a-zA-Z_]
  • \D cualquier cosa que no sea un número
  • \S cualquier cosa que no sea un espacio

El carácter de punto . coincide con todo menos nuevas líneas. Si necesita usar un . literal entonces tendrás que escaparlo.

Ejemplo: escapando caracteres especiales

    # If we don't escape, the letter will match
    "5a5".match(/\d.\d/)
    # In this case only the literal dot matches
    "5a5".match(/\d\.\d/) # nil
    "5.5".match(/\d\.\d/) # match

Modificadores

Hasta ahora solo hemos podido hacer coincidir un solo personaje a la vez. Para hacer coincidir varios caracteres, podemos usar modificadores de patrones.

+ 1 o más * 0 o más ? 0 o 1 {3,5} entre 3 y 5

Podemos combinar todo lo que hemos aprendido hasta ahora para crear expresiones regulares más complejas.

Ejemplo: ¿Parece una dirección IP?

    # Note that this will also match some invalid IP address
    # like 999.999.999.999, but in this case we just care about the format.
    def ip_address?(str)
      # We use !! to convert the return value to a boolean
      !!(str =~ /^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/)
    end
    ip_address?("192.168.1.1")  # returns true
    ip_address?("0000.0000")    # returns false

Coincidencia exacta de strings

Si necesita coincidencias exactas, necesitará otro tipo de modificador. Veamos un ejemplo para que veas de qué estoy hablando:

    # We want to find if this string is exactly four letters long, this will
    # still match because it has more than four, but it's not what we want.
    "Regex are cool".match /\w{4}/
    # Instead we will use the 'beginning of line' and 'end of line' modifiers
    "Regex are cool".match /^\w{4}$/
    # This time it won't match. This is a rather contrived example, since we could just
    # have used .size to find the length, but I think it gets the idea across.

Si desea hacer coincidir estrictamente al comienzo de un string y no solo en cada línea (después de \n), debe usar \A y \Z en lugar de ^ y $.

Grupos Capture

Con los grupos capture, podemos capturar parte de una coincidencia y reutilizarla más tarde. Para capturar una coincidencia incluimos la parte que queremos capturar entre paréntesis.

Ejemplo: Analizando un archivo Log

    Line = Struct.new(:time, :type, :msg)
    LOG_FORMAT = /(\d{2}:\d{2}) (\w+) (.*)/
    def parse_line(line)
      line.match(LOG_FORMAT) { |m| Line.new(*m.captures) }
    end
    parse_line("12:41 INFO User has logged in.")
    # This produces objects like this:
    # <struct line="" time="12:41" ,="" type="INFO" msg="User has logged in.">
    </struct>

En este ejemplo, usamos .match en lugar de =~.

Este método devuelve un objeto MatchData si hay una coincidencia, nil en caso contrario. La clase MatchData tiene muchos métodos útiles, consulte la documentacion para obtener más información.

Si solo desea un valor booleano (true/false), ¿puede usar el método match?, que está disponible desde Ruby 2.4. Esto también es más rápido que match, ya que Ruby no necesita crear un objeto MatchData.

Puede acceder a los datos capturados utilizando el método .captures o tratando el objeto MatchData como un arreglo, el índice cero tendrá la coincidencia completa y los índices consiguientes contendrán los grupos coincidentes.

Si desea el primer grupo de captura, puede hacer esto:

    m = "John 31".match /\w+ (\d+)/
    m[1]
    # 31

También puede tener grupos que no capturan. Le permitirán agrupar expresiones sin penalizar el rendimiento. También puede encontrar útiles los grupos con nombre para facilitar la lectura de expresiones complejas.

`(?:...)` grupo de no captura `(?...)` grupo nombrado

Ejemplo: grupos con nombre

    m = "David 30".match /(?<name>\w+) (?<age>\d+)/
    m[:age]
    # => "30"
    m[:name]
    # => "David"
    </age></name>

Un grupo con nombre devuelve un objeto MatchData al que puede acceder para leer los resultados.

Look Ahead & Look Behind

Esta es una técnica más avanzada que puede no estar disponible en todas las implementaciones de expresiones regulares. El motor de expresiones regulares de Ruby es capaz de hacer esto, así que veamos cómo aprovecharlo.

Look ahead nos permite echar un vistazo y ver si hay una coincidencia específica antes o después.

`(?=pat)` lookahead positivo `(?<=pat)` lookbehind positivo `(?!pat)` lookahead negativo `(? lookbehind negativo

Ejemplo: ¿hay un número precedido por al menos una letra?

    def number_after_word?(str)
      !!(str =~ /(?<=\w) (\d+)/)
    end
    number_after_word?("Grade 99")

Clase Regex de Ruby

Las expresiones regulares de Ruby son instancias de la clase Regexp. La mayoría de las veces no utilizará esta clase directamente, pero es bueno saber.

    puts /a/.class
    # Regexp

Un posible uso es crear una expresión regular a partir de un string:

    regexp = Regexp.new("a")

Otra forma de crear una expresión regular:

    regexp = %r{\w+}

Opciones de Regex

`i` no sensible a las mayusculas `m` punto coincide con nueva línea `x` ignore espacios en blanco

Para usar estas opciones, agregue la letra al final de la expresión regular, después del cierre /.

    "abc".match?(/[A-Z]/i)

Las expresiones regulares complejas de Ruby pueden resultar bastante difíciles de leer, por lo que será útil dividirlas en varias líneas. Podemos hacer esto usando el modificador 'x'. Este formato también le permite utilizar comentarios dentro de su expresión regular.

    LOG_FORMAT = %r{
      (\d{2}:\d{2}) # Time
      \s(\w+)       # Event type
      \s(.*)        # Message
    }x

Ruby regex: Poniendo todo junto

Las expresiones regulares se pueden usar con muchos métodos Ruby.

  • .split
  • .scan
  • .gsub
  • y muchas más

Ejemplo: haga coincidir todas las palabras de un string usando .scan

    "this is some string".scan(/\w+/)
    # => ["this", "is", "some", "string"]

Ejemplo: extraer todos los números de un string.

    "The year was 1492.".scan(/\d+/)
    # => ["1492"]

Ejemplo: poner en mayúscula todas las palabras de un string

    str = "lord of the rings"
    str.gsub(/\w+/) { |w| w.capitalize }
    # => "Lord Of The Rings"

Ejemplo: validar una dirección de correo electrónico

    email = "[email protected]"
    !!email.match(/\A[\w.+-]+@\w+\.\w+\z/)
    # true

Este último ejemplo usa !! para convertir el resultado en un valor booleano (true/false), alternativamente, puede usar match? en Ruby 2.4+ que ya hace esto por ti y también es más rápido.

⚠️ **GitHub.com Fallback** ⚠️