En Ruby puedes seleccionar caracteres concretos de una cadena mediante índices, rangos, el método chars, subcadenas y expresiones regulares. La elección depende de si necesitas obtener una posición, un intervalo, cada carácter individual o todos los fragmentos que cumplan un patrón.
Una cadena de texto puede comportarse de forma similar a un arreglo de caracteres, por lo que es posible acceder a posiciones específicas, recorrer sus caracteres, dividirla en partes y aplicar expresiones regulares para localizar patrones complejos.
Seleccionar un carácter por su posición
Puedes obtener los caracteres de una posición específica igual que con los arreglos. Por ejemplo, modifica el archivo para mostrar los primeros 4 caracteres:
str = "Hola mundo"puts str[0]puts str[1]puts str[2]puts str[3]
Si ejecutas ese programa deberías ver algo similar a esto:
Hola
Ruby utiliza índices que comienzan en cero, de modo que el primer carácter está en la posición 0, el segundo en la posición 1 y el tercero en la posición 2.
El espacio también hace parte de la longitud.
Obtener la longitud de una cadena
Empecemos creando un archivo llamado strings.rb y veamos primero como obtener la longitud de la cadena:
str = "Hola mundo"puts str.length
Si ejecutas ese programa deberías ver que la longitud de la cadena es 10.
El método length devuelve el número de caracteres de la cadena y permite comprobar cuántas posiciones existen antes de seleccionar un carácter concreto.
Recorrer todos los caracteres
Para recorrer cada uno de los caracteres de una cadena puedes hacer lo siguiente:
str = "Hola mundo"chars = str.chars # ["H", "o", "l", "a", " ", "m", "u", "n", "d", "o"]chars.each do |c| puts cend
El método chars retorna un arreglo con todos los caracteres de la cadena, y después iteramos sobre ese arreglo normalmente.
Fíjate que el espacio en blanco también cuenta como un caracter.
Esta técnica resulta útil cuando necesitas examinar, filtrar o transformar cada carácter por separado.
Seleccionar varios caracteres con rangos
Ruby permite seleccionar una parte continua de una cadena usando un rango de posiciones. Esta forma es apropiada cuando conoces la posición inicial y final del fragmento que quieres obtener.
str = "Hola mundo"puts str[0..3]
El rango incluye las posiciones inicial y final, por lo que el resultado contiene los cuatro primeros caracteres.
También puedes seleccionar una cantidad determinada de caracteres desde una posición:
str = "Hola mundo"puts str[0, 4]
En este caso, el primer número indica la posición inicial y el segundo indica cuántos caracteres se deben seleccionar.
Convertir una cadena en un arreglo de caracteres
No siempre queremos crear un arreglo a partir de sus caracteres. En ocasiones es útil dividir una cadena por otro caracter como espacio o coma.
Para convertir una cadena completa en un arreglo de caracteres, utiliza chars:
str = "Hola mundo"chars = str.charsputs chars
El resultado es un arreglo cuyos elementos corresponden a los caracteres individuales, incluido el espacio:
["H", "o", "l", "a", " ", "m", "u", "n", "d", "o"]
Una vez convertida la cadena en arreglo, puedes acceder a cualquier carácter utilizando la misma sintaxis de índices:
chars[0]chars[3]
Dividir una cadena con split
Partir cadenas no siempre significa seleccionar caracteres individuales. En ocasiones es útil dividir una cadena por otro caracter como espacio o coma.
Para eso podemos utilizar el método split.
El método split recibe el caracter por el que quieres partir la cadena.
"Hola mundo".split(" ")# ["Hola", "mundo"]También puedes partir una cadena por comas:
"prueba,separar,cadenas".split(",")# ["prueba", "separar", "cadenas"]El método split retorna un arreglo con las partes obtenidas después de dividir la cadena.
Si quieres dividir una cadena por espacios, tabulaciones o saltos de línea, una expresión regular puede describir esos separadores de forma más flexible.
Unir nuevamente las partes con join
Si quieres unir un arreglo en una cadena puedes utilizar el método join:
["Hola", "mundo"].join(" ")# "Hola mundo"El método join recibe un argumento que es el caracter que se va a utilizar para separar los elementos.
Por ejemplo, podemos unir un arreglo con guiones:
["Hola", "mundo"].join("-")# "Hola-mundo"De esta forma, split divide una cadena en partes y join vuelve a combinar los elementos de un arreglo en una cadena.
Eliminar espacios antes de seleccionar caracteres
Hay momentos en los que necesitas convertir una cadena de caracteres en un array de Ruby.
Tiene espacios al principio. Tiene una tabulación en medio y un salto de línea.
Entonces, lo primero que podemos hacer en este string es eliminar los espacios tanto del principio como del final.
Para ello podemos utilizar el método Strip y, si le ponemos el signo de exclamación al final, esto lo que va hacer es que va a guardar el valor resultante de esta operación dentro de la propia variable string.
Así, no tenemos que usar una variable intermedia.
string.strip!
Una vez hecho esto, queremos también eliminar todos los espacios que hay dentro de esa frase porque no nos interesa tenerlos cuando vayamos a convertir este string en un array.
Como hemos visto en vídeos anteriores, esto lo podemos hacer utilizando el método gsub.
El método gsub recibe dos argumentos: uno es la string que voy a buscar y el otro es la string por la que la voy a sustituir.
string.gsub(" ", "")Después de limpiar la cadena, puedes utilizar chars para obtener sus caracteres o split para dividirla según un separador.
Otros métodos útiles para trabajar con caracteres
Sobre las cadenas de texto podemos llamar una gran cantidad de métodos y operadores interesantes.
- length devuelve la longitud de la cadena.
- chars convierte la cadena en un arreglo de caracteres.
- split divide la cadena según un separador.
- join une los elementos de un arreglo en una cadena.
- gsub reemplaza las ocurrencias de un patrón.
- capitalize cambia la primera letra a mayúscula.
"hola mundo".capitalize # Hola mundo
Returna true si la cadena está vacía.
"".empty?
Reemplaza las ocurrencias en el texto:
"Hola mundo".gsub("mundo", "Germán") # Hola GermánEstos métodos permiten preparar una cadena antes de seleccionar caracteres específicos o buscar patrones.
Expresiones regulares para seleccionar caracteres por patrón
Es el momento de comprobar si una cadena satisface una descripción, que llamaremos patrón.
En estos patrones existen algunos caracteres y combinaciones de caracteres que tienen un significado especial.
El término común para estos patrones que utilizan este extraño vocabulario es expresión regular.
En Ruby, como en Perl, normalmente están rodeadas por barras inclinadas en vez de por comillas dobles.
Si nunca antes se ha trabajado con expresiones regulares, es probable que parezcan cualquier cosa excepto regulares, pero sería inteligente dedicar algún tiempo a familiarizarse con ellas.
Tienen un poder expresivo en su concisión que puede evitar muchos dolores de cabeza y muchas líneas de código si se necesita realizar coincidencia de patrones o cualquier otro tipo de manipulación con cadenas de texto.
Caracteres especiales en expresiones regulares
| Símbolo | Descripción |
|---|---|
| [] | Especificación de rango, por ejemplo [a-z] representa una letra en el rango de la a a la z. |
| \w | Letra o dígito; es lo mismo que [0-9A-Za-z]. |
| \W | Ni letra, ni dígito. |
| \s | Espacio, es lo mismo que [ \t\n\r\f]. |
| \S | No espacio. |
| \d | Dígito; es lo mismo que [0-9]. |
| \D | No dígito. |
| \b | Límite de palabra cuando no aparece en una especificación de rango. |
| \B | No límite de palabra. |
| * | Cero o más repeticiones de lo que precede. |
| + | Una o más repeticiones de lo que precede. |
| [m,n] | Al menos m y como máximo n de lo que precede. |
| ? | Al menos una repetición de lo que precede; es lo mismo que [0,1]. |
| | | Puede coincidir con lo que precede o con lo que sigue. |
| () | Agrupamiento. |
Coincidencia de un carácter concreto
Los patrones más simples buscan coincidencias con cadenas exactas.
/a/
Esta expresión regular busca la letra a en cualquier parte de la cadena.
El siguiente paso en complejidad es ., que coincide con cualquier caracter excepto un salto de línea.
/./
Pero si “.” coincide con cualquier caracter, ¿cómo buscar una coincidencia con el caracter “.”?
Necesitas utilizar un “escape” para decirle a la expresión regular que quieres hacerla coincidir de manera exacta, no usar su comportamiento especial.
Al igual que en las cadenas, las expresiones regulares usan la barra invertida, \, para “escapar” los comportamientos especiales.
Por lo tanto, para hacer coincidir un ., necesitas la expresión regular \.
Escapar caracteres especiales
Para incluir comillas simples o dobles de manera literal en una cadena puedes utilizar \ para “escaparlas”.
Esto quiere decir que si quieres incluir una barra invertida, necesitas duplicarla: "\\".
Ten cuidado con el hecho de que la representación impresa de una cadena no es equivalente a la cadena misma, ya que la representación muestra las barras utilizadas para “escapar” caracteres, es decir, para sean interpretados en su sentido literal, no como caracteres especiales.
Para coincidir literalmente con una barra invertida necesitas escribir cuatro barras invertidas en una cadena:
"\\\\"
Para hacer coincidir de manera literal un carácter que normalmente tiene un significado especial, también puedes utilizar una clase de caracteres:
/a[.]c/
Esto busca el texto a.c y trata el punto como un carácter literal.
También es posible buscar un asterisco literal:
/.[*]c/
Esto funciona para la mayoría de los metacaracteres de las expresiones regulares: $ . | ? * + ( ) [ {.
Desafortunadamente, existen unos pocos caracteres que tienen un significado especial incluso dentro de una clase de caracteres y deben manejarse con barras invertidas para escaparlos: ] \ ^ y -.
Clases de caracteres
Existe una serie de patrones especiales que coinciden con más de un caracter.
- \d coincide con cualquier dígito.
- \s coincide con cualquier espacio en blanco, por ejemplo, espacio simple, tabulador o salto de línea.
- [abc] coincide con a, b o c.
- [^abc] coincide con todo menos con a, b o c.
Una especificación de rango permite seleccionar un carácter perteneciente a un intervalo:
/[a-z]/
La expresión regular [a-z] representa una letra en el rango de la a a la z.
La clase [0-9] selecciona un dígito.
/[0-9]/
La clase \d expresa la misma idea:
/\d/
Para crear una expresión regular que contenga \d o \s necesitas escapar la \ en la cadena, por lo que debes escribir "\\d" o "\\s".
Alternativas con |
Puedes utilizar una disyunción para elegir entre uno más patrones alternativos.
Por ejemplo, abc|d coincidirá tanto con ‘“abc”’, como con "duna".
Ten en cuenta que la precedencia de | es baja, por lo que abc|xyz coincidirá con abc o xyz, no con abcyz o abxyz.
Al igual que en expresiones matemáticas, si el valor de | se vuelve confuso, utiliza paréntesis para dejar claro qué es lo que quieres:
/c(ó|o)mo/
Esta expresión coincide con cómo y como.
Repetición de caracteres
El siguiente paso en términos de poder implica controlar cuántas veces queremos que se encuentre un patrón:
- ?: 0 o 1.
- +: 1 o más.
- *: 0 o más.
Por ejemplo, el patrón f.*z selecciona una cadena que empieza con f, continúa con cualquier cantidad de caracteres y llega hasta una z.
foozboozerf.*z
Se obtiene foozbooz en vez de fooz porque las expresiones regulares tratan de obtener la coincidencia más larga posible.
El modificador ? permite que el elemento anterior aparezca cero o una vez.
/CC?/
El modificador + selecciona una o más repeticiones:
/[0-9]+/
El modificador * selecciona cero o más repeticiones:
/[^a-z]*/
Anclas de inicio y final
Por defecto, las expresiones regulares buscarán una coincidencia en cualquier parte de una cadena.
Suele ser útil anclar una expresión regular para que solo busque coincidencias al inicio o al final.
- ^ busca la coincidencia al inicio de la cadena.
- $ busca la coincidencia al final de la cadena.
Para forzar que una expresión regular coincida con una cadena completa, ánclala usando tanto ^ como $.
/^f[A-Z][^a-z]*$/
Esta expresión regular describe una cadena que comienza con una f minúscula, a la que sigue exactamente una letra mayúscula y opcionalmente cualquier cosa detrás de ésta, siempre y cuando no haya más letras minúsculas.
También puedes coincidir el límite entre palabras con \b.
/\bsum\b/
Esta expresión evita la coincidencia con summarise, summary, rowsum y otras.
Extraer una hora dentro de una cadena
A continuación se muestra un patrón para aislar la hora de un campo limitada por dos puntos.
/[0-9]+:[0-9]+(:[0-9]+)?/
El patrón selecciona una o más cifras, seguidas de dos puntos, otras cifras y una parte opcional que contiene los segundos.
Seleccionar un número hexadecimal
Supongamos que queremos comprobar si una cadena satisface una descripción.
¿Contiene la cadena un número hexadecimal entre ángulos?
No hay problema.
def chab(s) (s =~ /<0[Xx][\dA-Fa-f]+>/) != nilend
El patrón busca un signo menor que, la secuencia 0x o 0X, uno o más caracteres hexadecimales, y finalmente un signo mayor que.
chab "Este no es"# falsechab "¿Puede ser esta? (0x35)"# falsechab "¿O esta? <0x38z7e>"# falsechab "OK esta si; <0xfc0004>"# true
El operador =~
=~ es el operador de coincidencia con expresiones regulares; devuelve la posición en la cadena donde se ha producido una coincidencia o nil si no la hay.
"abcdef" =~ /d/# 3"aaaaaa" =~ /d/# nil
En el primer caso, la letra d aparece en la posición 3.
En el segundo caso no aparece ninguna letra d, por lo que el resultado es nil.
Este operador permite comprobar si una cadena contiene un carácter o patrón sin tener que recorrer manualmente todos sus caracteres.
Resaltar las coincidencias encontradas
A continuación se presenta un pequeño programa que nos permitirá experimentar con las expresiones regulares.
Almacenémoslo como regx.rb, se ejecuta introduciendo en la línea de comandos ruby regx.rb.
st = "\033[7m"en = "\033[m"while TRUE print "str> " STDOUT.flush str = gets break if not str str.chop! print "pat> " STDOUT.flush re = gets break if not re re.chop! str.gsub! re, "#{st}\\{en}" print str, "\n"endprint "\n"El programa necesita dos entradas, una con la cadena y otra con la expresión regular.
La cadena se comprueba contra la expresión regular, a continuación muestra todas las partes de la cadena que coinciden con el patrón en vídeo inverso.
str> foobarpat> ^fo+foobar~~~
Lo resaltado es lo que aparecerá en vídeo inverso como resultado de la ejecución del programa.
La cadena ’~~~’ es en beneficio de aquellos que usen visualizadores en modo texto.
Probemos algunos ejemplos más.
str> abc012dbcd555pat> \dabc012dbcd555
Sorprendentemente y como indica la tabla al principio de este capítulo: \d no tiene nada que ver el carácter d, sino que realiza la coincidencia con un dígito.
Elegir entre índices, rangos y expresiones regulares
| Necesidad | Recurso |
|---|---|
| Obtener un carácter por posición | str[índice] |
| Obtener varios caracteres consecutivos | str[inicio..fin] |
| Obtener una cantidad desde una posición | str[inicio, cantidad] |
| Convertir en caracteres individuales | str.chars |
| Dividir por un separador | str.split |
| Seleccionar por patrón | expresión regular |
| Comprobar una coincidencia | =~ |
| Reemplazar coincidencias | gsub |
Ejemplo completo
El siguiente ejemplo combina varias técnicas para seleccionar caracteres específicos de una cadena:
str = "Ruby 123"puts str[0]puts str[0..3]puts str.charsputs str.split(" ")puts str =~ /\d/puts str.gsub(/\d/, "*")La primera operación selecciona un carácter por índice.
La segunda selecciona un rango de caracteres.
La tercera convierte la cadena en un arreglo de caracteres.
La cuarta divide la cadena usando el espacio como separador.
La quinta localiza el primer dígito mediante una expresión regular.
La sexta reemplaza cada dígito encontrado por un asterisco.
Prueba cada uno de los ejemplos en IRB.
[EN] Just-in-Time Compiling Ruby Regexps on TruffleRuby / @eregon and @djoooooe
