Carregando
Todos os artigos
Programação 17 min de leitura

Métodos de String em Ruby: Um Guia Visual e Prático

Aprenda os métodos de string do Ruby de forma visual: índices e fatias, maiúsculas e minúsculas, aparar espaços, métodos com ! e as suas pegadinhas, busca, sub vs. gsub, split e join, alinhar texto, conversões, uma medição real e exercícios com respostas.

nbkhope
Nesta página

Nomes, mensagens, senhas, páginas da web, conversas de chat, arquivos CSV: uma parte enorme do que os programas manipulam é texto. Em Ruby, um pedaço de texto é uma String, e as strings vêm com uma caixa de ferramentas impressionante de métodos prontos para mudar maiúsculas e minúsculas, aparar espaços, buscar, substituir, separar e muito mais.

Neste guia você vai ver como uma string é organizada, conhecer os métodos que vai usar todo dia, aprender os métodos com "bang" (!) e as suas pegadinhas, alinhar um cupom fiscal e medir por que um jeito popular de montar strings fica dramaticamente lento. Exercícios com respostas fecham o artigo.

Todas as saídas abaixo vêm de execuções reais com o Ruby 3.3. Os exemplos usam p para imprimir, porque ele mostra as strings entre aspas, e assim dá para ver cada espaço.

Uma string é uma fileira de caracteres

Uma string é uma fileira de caracteres, numerada a partir do 0, ou do -1 no fim. A palavra Abacate tem A no índice 0 e e no índice 6, que também é o índice -1. palavra[0, 4] começa no 0 e pega 4 caracteres, dando "Abac". palavra[4..] vai do índice 4 até o fim, dando "ate". palavra[0] é "A", palavra[-1] é "e", e um índice depois do fim, como palavra[20], dá nil em vez de erro

Cada caractere tem uma posição numerada, o seu índice, contando a partir do 0. Índices negativos contam a partir do fim. Os colchetes pegam caracteres ou fatias inteiras:

1palavra = "Abacate"
2p palavra[0]
3p palavra[3]
4p palavra[-1]
5p palavra[0, 4]
6p palavra[0..3]
7p palavra[4..]
8p palavra.length
9p palavra[20]
1"A"
2"c"
3"e"
4"Abac"
5"Abac"
6"ate"
77
8nil
  • palavra[0, 4] quer dizer "comece no 0 e pegue 4 caracteres".
  • palavra[0..3] quer dizer "do índice 0 ao índice 3, incluindo os dois".
  • palavra[4..] quer dizer "do 4 até o fim".

Quantos métodos!

Quantos métodos uma string tem? O próprio Ruby conta:

1puts "Uma string responde a #{"Uma String".methods.size} métodos"
2puts "#{String.instance_methods(false).size} deles são definidos pela própria String"
3p "Uma String".methods.sort.first(8)
4p "Uma String".methods.grep(/case/).sort
1Uma string responde a 181 métodos
2133 deles são definidos pela própria String
3[:!, :!=, :!~, :%, :*, :+, :+@, :-@]
4[:casecmp, :casecmp?, :downcase, :downcase!, :swapcase, :swapcase!, :upcase, :upcase!]

O grep(/case/) filtra a lista e deixa só os métodos com "case" no nome, um truque útil quando você se lembra do método pela metade. As strings têm mais métodos próprios que qualquer outra classe básica:

Quantos métodos cada tipo de objeto do Ruby tem? Métodos que cada classe define ela mesma, contados no Ruby 3.3.2: String 133, Array 108, Hash 72, Integer 65, Symbol 29. Contando também os herdados, uma string responde a 181 métodos. Ninguém decora todos: aprenda os vinte e poucos que você vai usar no dia a dia e consulte o resto

Calma, você não vai precisar dos 181. Aqui está um mapa dos que você mais vai usar, agrupados por tarefa:

Um mapa dos métodos de string do dia a dia. Maiúsculas: upcase, downcase, capitalize, swapcase. Aparar e arrumar: strip, lstrip e rstrip, squeeze, chomp. Perguntar: include?, start_with?, empty?, length. Encontrar: index e rindex, count, scan, fatias com colchetes. Substituir: sub e gsub, tr, delete, reverse. Separar e juntar: split, chars, lines, Array#join. Alinhar: ljust e rjust, center, format, asterisco para repetir. Converter: to_i e to_f, to_sym, Integer(), to_s

Maiúsculas e minúsculas

1p "Olá".upcase
2p "MUNDO".downcase
3p "Como VAI você hOJE?".capitalize
4p "Olá Mundo".swapcase
5p "ação e coração".upcase
1"OLÁ"
2"mundo"
3"Como vai você hoje?"
4"oLÁ mUNDO"
5"AÇÃO E CORAÇÃO"

O capitalize deixa o primeiro caractere maiúsculo e todo o resto minúsculo. E os métodos do Ruby entendem letras acentuadas, não só de A a Z.

Aparando espaços

Textos digitados por pessoas (ou copiados de algum lugar) muitas vezes vêm com espaços sobrando, no começo ou no fim:

Aparando espaços: strip, lstrip e rstrip. O texto "   , E então   " tem três espaços de cada lado, mostrados como pontos. texto.lstrip tira os espaços da esquerda, dando ", E então   ". texto.rstrip tira os da direita, dando "   , E então". texto.strip tira os dois, dando ", E então". O lstrip apara o lado esquerdo, do começo; o rstrip o direito, do fim; e o strip os dois. Perfeito para limpar o que os usuários digitam
1com_espacos = "   , E então   "
2p com_espacos.lstrip
3p com_espacos.rstrip
4p com_espacos.strip
5p "  espaços   bagunçados  ".squeeze(" ").strip
1", E então   "
2"   , E então"
3", E então"
4"espaços bagunçados"

O squeeze(" ") junta sequências de espaços repetidos num só.

Métodos com !: upcase vs. upcase!

Nenhum dos métodos até aqui muda a string original: eles entregam uma string nova. A maioria tem um gêmeo terminado em ! (o "bang") que muda a string no lugar:

Sem ! você ganha uma string nova; com ! a original muda. grito = frase.upcase cria uma string novinha, EU SUBI NUMA ÁRVORE., e a frase continua com Eu subi numa árvore. frase.upcase! muda a mesma string no lugar, e a frase passa a ter EU SUBI NUMA ÁRVORE. Pegadinha: um método com ! devolve nil quando não havia nada para mudar, então "OLÁ".upcase! dá nil
1frase = "Eu subi numa árvore."
2outra_frase = frase.upcase
3
4puts "Frase original: #{frase}"
5puts "Outra frase:    #{outra_frase}"
6
7frase.upcase!
8puts "Agora a frase:  #{frase}"
Frase original: Eu subi numa árvore.
Outra frase:    EU SUBI NUMA ÁRVORE.
Agora a frase:  EU SUBI NUMA ÁRVORE.

O #{...} dentro de aspas duplas é a interpolação de strings: o Ruby roda o código entre as chaves e coloca o resultado no texto.

Pegadinha 1: métodos com ! podem devolver nil

Quando não há nada para mudar, um método com ! devolve nil em vez da string:

grito = "OLÁ"
p grito.upcase
p grito.upcase!
"OLÁ"
nil

Então nunca encadeie nada depois de um método com !:

nome = "ADA"
puts nome.upcase!.length
1encadeado.rb:2:in `<main>': undefined method `length' for nil (NoMethodError)
2
3puts nome.upcase!.length
4                 ^^^^^^^

("Método length indefinido para nil.")

Pegadinha 2: dois nomes, uma string

Atribuir uma string a uma segunda variável não a copia. Os dois nomes apontam para a mesma string, então mudá-la no lugar por um nome muda também para o outro:

Dois nomes, uma string. original = +"Ruby", depois mesma_string = original, depois mesma_string << "!": os dois nomes, original e mesma_string, apontam para a mesma string, que agora é "Ruby!". Atribuir uma string a outra variável não a copia: os dois nomes apontam para o mesmo objeto, então mudá-la por um nome muda para os dois. Use .dup quando precisar de uma cópia separada

Esse é o verdadeiro motivo para ter cuidado com os métodos ! e com o <<: você pode estar mudando uma string que outras partes do programa ainda estão usando. (Você vai encontrar isso no Exercício 1.)

Pegadinha 3: strings congeladas

Muitos arquivos Ruby começam com um comentário mágico que deixa toda string literal congelada, ou seja, imutável:

1# frozen_string_literal: true
2
3saudacao = "olá"
4saudacao << " mundo"
congelada.rb:4:in `<main>': can't modify frozen String: "olá" (FrozenError)

("Não dá para modificar a String congelada.") Strings congeladas são mais rápidas e seguras, e as próximas versões do Ruby estão caminhando para congelar os literais por padrão. Quando você precisar de uma string que possa mudar, comece com +"olá" ou "olá".dup.

Montando strings

1nome = "Ada"
2sobrenome = "Lovelace"
3nascimento = 1815
4
5p nome + " " + sobrenome
6p "#{nome} #{sobrenome} nasceu em #{nascimento}"
7p "Ha" * 3
8p nome + ", você faria " + (2026 - nascimento).to_s + " anos em 2026"
9saudacao = +"Olá"
10saudacao << ", " << nome << "!"
11p saudacao
12p format("%-10s|%6.2f", "Pi", 3.14159)
1"Ada Lovelace"
2"Ada Lovelace nasceu em 1815"
3"HaHaHa"
4"Ada, você faria 211 anos em 2026"
5"Olá, Ada!"
6"Pi        |  3.14"
  • + junta strings, mas os dois lados precisam ser strings, por isso o .to_s no número.
  • A interpolação "#{...}" converte sozinha e costuma ficar mais legível.
  • O << (a "pá", shovel) acrescenta numa string existente, mudando-a no lugar.
  • O format dá controle preciso: %-10s completa o texto até 10 caracteres, %6.2f mostra um número com 2 casas decimais em 6 caracteres.

Por que o += fica lento

Montar uma string grande pedaço por pedaço é comum: um relatório, uma exportação CSV, uma página da web. O inocente relatorio += pedaco tem um custo escondido. A cada vez, o Ruby monta uma string novinha com tudo o que já havia, mais o pedaço novo. Medimos isso:

Montando uma string grande: += é quadrático, << não. Juntando linhas curtas numa string só, medido para este artigo com Ruby 3.3.2 no Windows, mediana de 5 execuções, depois a mediana de 3 execuções do programa, em escala logarítmica. 10.000 pedaços: o += levou 0,34 segundo, o << 0,6 milissegundo e o join 0,2 milissegundo. 25.000 pedaços: 1,2 segundo, 2,0 e 0,6 milissegundos. 50.000 pedaços: 4,2 segundos, 4,7 e 1,5 milissegundos. 100.000 pedaços: 16,1 segundos, 9,6 e 3,7 milissegundos. O += cria uma string novinha, cada vez maior, a cada passo, então o trabalho cresce com o quadrado da quantidade de pedaços. O << acrescenta na mesma string, e o join monta tudo de uma vez

Com 100.000 pedaços, o += levou 16 segundos, enquanto o << levou menos de 10 milissegundos: umas 1.700 vezes mais rápido. Repare no padrão: dobrar os pedaços de 50.000 para 100.000 mais ou menos quadruplicou o tempo do += (de 4,2 para 16,1 segundos), mas só dobrou o tempo do <<. É isso que "quadrático" quer dizer, e é por isso que o += parece ótimo num teste rápido e depois se arrasta com dados de verdade.

1relatorio = ""
2pedacos.each { |pedaco| relatorio += pedaco }   # lento: uma string nova a cada vez
3
4relatorio = +""
5pedacos.each { |pedaco| relatorio << pedaco }   # rápido: acrescenta no lugar
6
7relatorio = pedacos.join                        # rápido e o mais simples

Buscando dentro de uma string

1titulo = "Bem-vindo ao meu mundo"
2p titulo.include?("mundo")
3p titulo.include?("terra")
4p titulo.start_with?("Bem")
5p titulo.end_with?("!")
6p titulo.index("o")
7p titulo.rindex("o")
8p titulo.count("o")
9p titulo.index("terra")
1true
2false
3true
4false
58
621
73
8nil

Métodos terminados em ? respondem perguntas de sim ou não com true ou false. O index dá a posição da primeira ocorrência (o rindex, da última), e nil quando não há nenhuma.

Substituindo: sub e gsub

sub troca a primeira ocorrência; gsub troca todas. Partindo de Eu amo massa, massa e mais massa, .sub("massa", "pizza") dá Eu amo pizza, massa e mais massa, trocando só a primeira, enquanto .gsub("massa", "pizza") dá Eu amo pizza, pizza e mais pizza, trocando as três. O g do gsub vem de global. Os dois também aceitam padrões, expressões regulares, como gsub(/\d/, "#") para esconder todos os dígitos
1frase = "Eu amo massa, massa e mais massa"
2p frase.sub("massa", "pizza")
3p frase.gsub("massa", "pizza")
4p frase
5p "24/10/2026".gsub("/", "-")
6p "banana".tr("an", "um")
7p "Ligue 5555-1234 ou 5555-9876".gsub(/\d/, "#")
8p "gato rato pato".gsub(/[gr]ato/) { |palavra| palavra.upcase }
1"Eu amo pizza, massa e mais massa"
2"Eu amo pizza, pizza e mais pizza"
3"Eu amo massa, massa e mais massa"
4"24-10-2026"
5"bumumu"
6"Ligue ####-#### ou ####-####"
7"GATO RATO pato"
  • O sub troca a primeira ocorrência; o gsub (de global) troca todas. Nenhum dos dois muda a original; o sub! e o gsub! mudam.
  • O tr troca caracteres um por um: todo a vira u e todo n vira m.
  • Os dois aceitam uma expressão regular (entre barras) para encontrar padrões: /\d/ é "qualquer dígito", /[gr]ato/ é "gato ou rato".
  • Com um bloco, o gsub deixa você calcular cada substituição.

Separando e juntando

split transforma uma string num array; join transforma de volta. "vermelho,verde,azul".split(",") dá o array "vermelho", "verde", "azul", e .join(",") transforma de volta numa string só. Sem argumento, o split quebra em qualquer espaço em branco: "a rápida raposa marrom".split dá quatro palavras
1p "vermelho,verde,azul".split(",")
2p "a rápida raposa marrom".split
3p "olá".chars
4p ["24", "10", "2026"].join("/")
5p "roma".reverse
6p "um\ndois\ntrês".lines
7p "banana".chars.tally
1["vermelho", "verde", "azul"]
2["a", "rápida", "raposa", "marrom"]
3["o", "l", "á"]
4"24/10/2026"
5"amor"
6["um\n", "dois\n", "três"]
7{"b"=>1, "a"=>3, "n"=>2}

Quando a string vira um array, todos os métodos de array ficam à sua disposição. O chars.tally conta quantas vezes cada letra aparece.

Alinhando o texto

O ljust, o rjust e o center completam uma string até uma largura fixa, perfeito para tabelas e cupons no terminal:

1itens = { "Café" => 3.5, "Pão de queijo" => 2.25, "Suco de laranja" => 4.0 }
2
3puts "CUPOM".center(26, "=")
4itens.each do |item, preco|
5  puts item.ljust(18, ".") + format("%.2f", preco).rjust(8)
6end
7puts "-" * 26
8puts "TOTAL".ljust(18) + format("%.2f", itens.values.sum).rjust(8)
1==========CUPOM===========
2Café..............    3.50
3Pão de queijo.....    2.25
4Suco de laranja...    4.00
5--------------------------
6TOTAL                 9.75

Convertendo de e para strings

Texto que vem de usuários ou de arquivos é sempre uma string, mesmo quando parece um número:

1p "42".to_i
2p "3.14".to_f
3p "42abc".to_i
4p "abc".to_i
5p "ruby".to_sym
6p 42.to_s
7p 255.to_s(2)
8begin
9  Integer("42abc")
10rescue ArgumentError => erro
11  puts "O Integer() recusou: #{erro.message}"
12end
142
23.14
342
40
5:ruby
6"42"
7"11111111"
8O Integer() recusou: invalid value for Integer(): "42abc"

Vazio, em branco e tamanho

1p "".empty?
2p "   ".empty?
3p "   ".strip.empty?
4p "abc".length
5p "é".length
6p "é".bytesize
1true
2false
3true
43
51
62

Uma string só de espaços não está vazia, então faça strip antes de verificar se a entrada está em branco. E o length conta caracteres, enquanto o bytesize conta bytes: uma letra acentuada é um caractere, mas dois bytes em UTF-8.

Hora de praticar

Tente fazer estes antes de espiar as respostas.

Exercício 1: Adivinhe a saída (aquecimento). O que as duas linhas com puts imprimem?

1original = +"Ruby"
2mesma_string = original
3mesma_string << "!"
4copia = original.upcase
5puts original
6puts copia

Exercício 2: Detector de palíndromos. Escreva palindromo?(texto) ignorando maiúsculas, espaços, pontuação e acentos, para que "Socorram-me, subi no ônibus em Marrocos!" conte.

Exercício 3: Frequência de palavras. Conte quantas vezes cada palavra aparece em "O gato e o rato e O pato", ignorando maiúsculas, e imprima um pequeno gráfico de barras, da mais comum para a menos comum.

Exercício 4: Título. Transforme "o senhor dos ANÉIS" em "O Senhor Dos Anéis".

Exercício 5: Filtro educado. Troque as palavras droga e puxa (com qualquer combinação de maiúsculas) por asteriscos do mesmo tamanho.

Exercício 6: Cifra de César (desafio). Ande cada letra 3 posições no alfabeto (a vira d, e z dá a volta até c), mantendo maiúsculas e minúsculas. Depois decifre de novo.

Quiz rápido

  1. Qual é a diferença entre sub e gsub?
  2. O que "OLÁ".upcase! devolve, e por quê?
  3. O que "Abacate"[-1] e "Abacate"[20] dão?
  4. Por que "42abc".to_i é arriscado para conferir o que o usuário digitou?
  5. Por que relatorio += pedaco fica lento dentro de um laço grande?

Respostas

Exercício 1: As duas variáveis apontam para a mesma string, então o << feito por mesma_string também mudou a original. O upcase (sem !) fez uma cópia separada:

1original = +"Ruby"
2mesma_string = original
3mesma_string << "!"
4copia = original.upcase
5puts original
6puts copia
Ruby!
RUBY!

Exercício 2: O unicode_normalize(:nfd) separa cada letra acentuada em letra + acento; aí uma expressão regular apaga tudo o que não for letra de a a z (inclusive os acentos), e basta comparar com o inverso:

1def palindromo?(texto)
2  letras = texto.unicode_normalize(:nfd).downcase.gsub(/[^a-z]/, "")
3  letras == letras.reverse
4end
5
6["arara", "Socorram-me, subi no ônibus em Marrocos!", "Ruby"].each do |texto|
7  puts "#{texto.inspect} -> #{palindromo?(texto)}"
8end
"arara" -> true
"Socorram-me, subi no ônibus em Marrocos!" -> true
"Ruby" -> false

Exercício 3: downcase, split, tally e depois ordenar pela contagem (e em ordem alfabética nos empates):

1texto = "O gato e o rato e O pato"
2contagens = texto.downcase.split.tally
3contagens.sort_by { |palavra, quantidade| [-quantidade, palavra] }.each do |palavra, quantidade|
4  puts "#{palavra.ljust(5)} #{'*' * quantidade} #{quantidade}"
5end
1o     *** 3
2e     ** 2
3gato  * 1
4pato  * 1
5rato  * 1

Exercício 4: Separe em palavras, deixe cada uma com inicial maiúscula e junte de volta:

1def titulo(texto)
2  texto.split.map(&:capitalize).join(" ")
3end
4
5puts titulo("o senhor dos ANÉIS")
O Senhor Dos Anéis

Exercício 5: Uma expressão regular montada a partir da lista de palavras: \b marca o limite das palavras, e o i ignora maiúsculas. O bloco mantém o tamanho:

1palavras_bloqueadas = %w[droga puxa]
2mensagem = "Droga, puxa vida, que droga de código!"
3
4limpa = mensagem.gsub(/\b(#{palavras_bloqueadas.join('|')})\b/i) { |palavra| "*" * palavra.length }
5puts limpa
*****, **** vida, que ***** de código!

Exercício 6: O tr consegue mapear o alfabeto inteiro numa cópia deslocada dele mesmo de uma vez só:

1def cesar(texto, deslocamento)
2  alfabeto = ("a".."z").to_a.join
3  deslocado = alfabeto.chars.rotate(deslocamento).join
4  texto.tr(alfabeto + alfabeto.upcase, deslocado + deslocado.upcase)
5end
6
7segredo = cesar("Olá, Mundo!", 3)
8puts segredo
9puts cesar(segredo, -3)
Roá, Pxqgr!
Olá, Mundo!

O á passa sem mudar, porque não está no alfabeto de a a z.

Quiz: (1) O sub troca a primeira ocorrência; o gsub troca todas. (2) nil, porque não havia nada para mudar: métodos com ! devolvem nil quando a string continua igual. (3) "e" (o último caractere) e nil (não existe caractere ali). (4) Ele devolve 42 em silêncio, e 0 para texto sem dígitos, então uma entrada inválida parece um número válido; use Integer() para validar. (5) Cada += cria uma string inteira nova com tudo o que já havia, então a cópia cresce com o quadrado da quantidade de pedaços; use << ou join.

Cola rápida

Eu quero…Escreva
Pegar um caractere / uma fatiatexto[0], texto[-1], texto[0, 4], texto[2..]
Mudar maiúsculas e minúsculasupcase, downcase, capitalize, swapcase
Aparar espaçosstrip, lstrip, rstrip, squeeze(" ")
Mudar a originala versão com !: upcase!, strip!, gsub! (devolve nil se nada mudar)
Inserir valores"Oi, #{nome}!"
Montar uma string granderelatorio << pedaco ou pedacos.join
Buscarinclude?, start_with?, end_with?, index, count
Trocar a primeira / todassub("a", "b") / gsub("a", "b")
Separar / juntarsplit(","), chars, lines / array.join(", ")
Alinhar textoljust(10), rjust(10), center(20, "="), format("%.2f", preco)
Converterto_i, to_f, to_s, to_sym, Integer("42")
Verificar se está em brancotexto.strip.empty?
Achar um método"texto".methods.grep(/case/)

Resumindo

Strings estão em todo lugar, e o Ruby tem um método para quase tudo o que você quiser fazer com elas:

  • Os índices começam no 0, índices negativos contam a partir do fim, e [início, tamanho] ou intervalos recortam fatias.
  • A maioria dos métodos devolve uma string nova. As versões com ! mudam a original, devolvem nil quando nada mudou e afetam todas as variáveis que apontam para aquela string.
  • O sub troca uma vez, o gsub em todo lugar, e os dois entendem expressões regulares.
  • O split e o join levam de strings para arrays e de volta.
  • ljust, rjust, center e format alinham o texto.
  • Monte strings grandes com << ou join, não com +=: com 100.000 pedaços, foram 16 segundos contra menos de 10 milissegundos no nosso teste.

Para mais Ruby, leia O Que É a Linguagem de Programação Ruby?, Arrays em Ruby e Entrada e Saída de Arquivos em Ruby.

Obrigado pela leitura!

Conhece alguém que vai gostar? Mande para essa pessoa.

Mais artigos

Leia a seguir

Comentários

Seja gentil e específico. Comentários com links de contas novas aparecem depois da análise de um moderador.