Uma das partes mais fundamentais de um programa é a capacidade de interagir com o usuário. Justamente por isso, neste artigo vamos conhecer as maneiras que existem para inserir e capturar dados em Ruby.

Apresentação


Até esse momento estamos inserindo dados nas variáveis, nas estruturas dos programas e etc., mas tudo isso está sendo feito diretamente no código. Ou seja, nós estamos alterando o código para mudar os dados, pré-definindo valores e não permitindo que esses dados sejam inseridos durante a execução do programa.

Mas como fazer para que a pessoa possa inserir dados durante a execução do programa? É aí que entram os comandos de entrada e saída de dados.

Entrada e saída (I/O, de Input/Output) são a forma como um programa conversa com o “mundo externo”.

  • Entrada (Input): é o processo de receber dados da pessoa por algum dispositivo de entrada, como o teclado, mouse, touchscreen, etc. Também pode acontecer ao receber dados de arquivos, bancos de dados, sensores e por aí vai. Todas as formas que as pessoas ou sistemas tem para fornecer dados ao programa são consideradas entradas.

  • Saída (Output): é o processo de enviar dados do programa para algum dispositivo de saída, como a tela do computador, impressora, alto-falantes, etc. Também pode acontecer ao salvar dados em arquivos, bancos de dados, ou enviar informações para outros sistemas. Todas as formas que o programa tem para comunicar informações para as pessoas ou outros sistemas são consideradas saídas.

Por exemplo, quando você digita algo no teclado, está fornecendo uma entrada para o programa. Quando o programa exibe uma mensagem na tela, está realizando uma saída.

Por isso é tão importante entender como funcionam os comandos de entrada e saída de dados em Ruby, para que possamos criar programas interativos e dinâmicos.

Streams padrão: STDIN, STDOUT e STDERR


Antes de aprender os comandos de Ruby que recebem e exibem informações, é importante entender como o programa se comunica com o ambiente fora dele, ou seja, como ele recebe dados e como ele envia respostas.

Para isso, Ruby (assim como quase todas as linguagens) utiliza um sistema chamado streams.

O que são streams?


Em português, podemos entender stream como um fluxo de dados, algo que está “correndo” de um lugar para outro, como um rio. Esses fluxos são caminhos pelos quais a informação passa:

  • A informação pode entrar no programa (como quando a pessoa digita algo no teclado).
  • A informação pode sair do programa (como quando o programa mostra alguma coisa na tela).
  • A informação pode ser uma mensagem de erro, que também sai do programa, mas de um jeito separado.

Esses caminhos já existem automaticamente quando você executa um programa Ruby no terminal. Por isso, chamamos eles de streams padrão.

Ruby possui três streams padrão.

STDIN: Standard Input (Em português: Entrada Padrão)


O STDIN é o fluxo de entrada, ou seja, por onde o programa recebe informações de fora. Na prática, quando você digita algo no teclado e aperta Enter, esse texto não vai “direto” para o Ruby. Ele passa pelo STDIN, que entrega esse texto ao programa.

Quando um comando para capturar informações é usado quem está trabalhando por trás dos panos é o STDIN.

STDOUT: Standard Output (Em português: Saída Padrão)


O STDOUT é o fluxo de saída normal. É por onde o programa envia para fora aquilo que ele quer mostrar à pessoa usuária. Por exemplo, quando você usa o comando puts para exibir uma mensagem na tela, essa mensagem é enviada pelo STDOUT.

Quando escrevemos:

puts "Olá, mundo!"

O Ruby envia a mensagem para o STDOUT, que por sua vez exibe no terminal.

É como se o programa dissesse: “Terminal, aqui está a mensagem que quero mostrar.”

STDERR: Standard Error (Em português: Erro Padrão)


O STDERR é o fluxo de saída de erros. Ele é usado para enviar mensagens de erro ou avisos quando algo dá errado no programa. Por exemplo, se você tentar dividir um número por zero, o Ruby vai gerar um erro e enviar essa mensagem pelo STDERR.

O STDERR é muito parecido com o STDOUT: também envia mensagens para o terminal. A diferença é que o STDERR é usado especificamente para erros, enquanto o STDOUT é usado para mensagens normais do programa.

Mas por que separar?

Porque isso permite que o terminal trate erros e saídas comuns de formas diferentes. Por exemplo, você pode redirecionar as mensagens de erro para um arquivo separado, enquanto mantém as mensagens normais na tela.

Em outras palavras


Imagine que um programa é como uma conversa:

  • Saída (output) é quando o programa fala com a pessoa.
  • Entrada (input) é quando a pessoa responde ao programa.

O Ruby precisa de comandos específicos para isso, e é aí que entram os comandos de entrada e saída de dados.

Como o Ruby mostra mensagens: o comando puts


Já conhecemos o comando puts, ele mostra um texto na tela e pula para a linha de baixo depois de escrever.

Por exemplo:

puts "Olá, mundo!"
puts "Bem-vindo ao Ruby!"

No terminal teremos algo como:

Olá, mundo!
Bem-vindo ao Ruby!

Observe que cada puts escreve uma linha e pula para a próxima ficando um texto embaixo do outro.

Por que ele pula linha sozinho?


Porque é assim que ele foi projetado. O nome puts vem de “put string” (colocar string), e ele sempre adiciona uma nova linha após a string que você quer mostrar.

Comando print


Outro comando que podemos usar para mostrar mensagens na tela é o print. Ele funciona de forma semelhante ao puts, mas com uma diferença importante: ele não pula para a linha de baixo depois de escrever.

Por exemplo:

print "Olá, mundo! "
print "Bem-vindo ao Ruby!"

No terminal teremos algo como:

Olá, mundo! Bem-vindo ao Ruby!

Observe que o print escreve tudo na mesma linha, sem pular para a próxima.

Isso é muito usado para perguntas, porque deixa o cursor na mesma linha do texto da pergunta, facilitando a leitura.

Quando usar puts ou print?


  • Use puts quando quiser que a mensagem seja exibida e pule para a próxima linha automaticamente.
  • Use print quando quiser que a mensagem seja exibida sem pular para a próxima linha, permitindo que o cursor fique na mesma linha.

Como o Ruby recebe informações: o comando gets


Agora que já sabemos como mostrar mensagens, vamos aprender a ler algo digitado pela pessoa usuária. Para isso, usamos o comando gets.

O gets lê uma linha de texto que a pessoa digita no teclado e retorna esse texto para o programa. Geralmente, usamos o gets junto com uma variável para armazenar o que foi digitado.

Ele:

  1. espera a pessoa digitar alguma coisa
  2. lê o texto
  3. devolve esse texto para o programa
  4. sempre inclui a tecla Enter no final, isso vira um \n (quebra de linha) no texto retornado.

O que isso quer dizer? Que se a pessoa digitar "Louise" e apertar Enter, o gets vai guardar o conteúdo "Louise\n" (com a quebra de linha no final).

Por que isso acontece? Porque o Enter é necessário para indicar que a pessoa terminou de digitar e quer enviar o texto para o programa e o gets captura tudo que foi digitado, incluindo essa quebra de linha criada pelo Enter.

Como resolver isso?


Para remover a quebra de linha que o gets adiciona no final do texto, usamos o método chomp.

chomp é um método que “corta” a última parte do texto, que no caso é a quebra de linha.

Lembrando que métodos são ações que os objetos podem fazer, já vimos isso no artigo anterior sobre tipos de dados. Então nesse caso, o objeto é a string retornada pelo gets e o método é o chomp que vai ter a ação de “cortar” (remover) a quebra de linha do final.

Com o uso do chomp, a informação guardada na variável pelo gets não terá mais a quebra de linha no final, ou seja, será exatamente o que a pessoa digitou, sem o \n no final.

Como usar o gets com o chomp?

Para responder a essa pergunta, vamos criar um pequeno programa que pergunta o nome da pessoa e depois exibe uma mensagem personalizada.

print "Qual é o seu nome? "
nome = gets.chomp
puts "Olá, #{nome}! Receba as boas-vindas ao Ruby!"

O que foi feito aqui?

Fizemos o seguinte:

  1. Usamos o print para perguntar o nome da pessoa, deixando o cursor na mesma linha.
  2. Usamos o gets.chomp para ler o que a pessoa digitou e remover a quebra de linha.
  3. Armazenamos o nome digitado na variável nome.
  4. Usamos o puts para exibir uma mensagem personalizada usando o nome que foi digitado.

Se você se lembrar da definição de método que foi apresentada no artigo Introdução à Programação: Tipos de Dados - Parte II, vai se lembrar que a forma de usar um método é:

objeto.nome_do_metodo(argumentos_opcionais)

Aqui, objeto é o objeto que você quer que faça algo, nome_do_metodo é o nome da ação que você quer que ele faça, e argumentos_opcionais são informações extras que você pode passar para o método, se for preciso.

No caso do chomp, o objeto é a string retornada pelo gets (o texto que a pessoa digitou), o nome do método é chomp e não precisamos passar nenhum argumento extra.

Por isso escrevemos:

gets.chomp

Isso significa: “Pegue o texto que a pessoa digitou (o objeto retornado pelo gets) e aplique o método chomp para remover a quebra de linha no final.”

E é assim que usamos o gets junto com o chomp para ler informações do usuário de forma limpa e sem quebras de linha indesejadas.

Dica: a partir de agora crie seus programas


Até aqui muito do que estava sendo apresentado poderia ser testado diretamente no console interativo do Ruby (IRB).

Não é que isso não seja mais possível, mas a partir de agora, para que você possa praticar melhor e criar programas mais completos, é interessante que você comece a criar arquivos .rb com seus códigos.

No artigo Introdução à Programação: Olá Mundo em Ruby você encontra um passo a passo de como criar uma pasta para o seu projeto e criar arquivos .rb para colocar seus códigos.

Dessa forma você poderá revisitar seus códigos sempre que quiser, além de criar uma linha do tempo dos seus aprendizados e da sua evolução na programação.

O IRB é ótimo para testes rápidos, mas criar arquivos .rb vai te ajudar a desenvolver suas habilidades de programação de forma mais estruturada.

Criando um programa com o conhecimento aprendido até aqui


Antes de avançar ainda mais, vamos criar um programa para trabalhar todos os conceitos que aprendemos até aqui.

Conceitos esses que estão sendo apresentados desde os artigos anteriores como:

O programa que vamos criar deve:

  1. Perguntar o nome do estudante
  2. Perguntar a turma do estudante
  3. Perguntar a disciplina que está sendo avaliada
  4. Perguntar a primeira nota
  5. Perguntar a segunda nota
  6. Perguntar a terceira nota
  7. Calcular a média das notas
  8. Exibir uma mensagem personalizada com o nome, turma, disciplina, média do estudante e se ele foi aprovado ou reprovado (considerando a média mínima para aprovação como 7.0)

Uma coisa boa é: Antes de começar a sair digitando o código, faça um rascunho do que você quer fazer, isso ajuda a organizar suas ideias e evita que você se perca no meio do caminho.

Vamos criar o arquivo 01-calcula-media.rb e colocar o seguinte código:

nome = ""
turma = ""
disciplina = ""
nota1 = 0.0
nota2 = 0.0
nota3 = 0.0
media = 0.0
aprovado = false

print "Qual é seu nome? "
nome = gets.chomp

print "Qual é a sua turma? "
turma = gets.chomp

print "Qual disciplina está sendo avaliada? "
disciplina = gets.chomp

print "Digite a primeira nota: "
nota1 = Float(gets.chomp)

print "Digite a segunda nota: "
nota2 = Float(gets.chomp)

print "Digite a terceira nota: "
nota3 = Float(gets.chomp)

media = (nota1 + nota2 + nota3) / 3

aprovado =  media >= 7.0

puts "Olá, #{nome} da turma #{turma}!"
puts "A média final da disciplina #{disciplina} é #{media.round(2)}."

puts "Sua situação é: #{aprovado ? 'Aprovado' : 'Reprovado'}."

Depois salve e execute o programa no terminal com o comando:

ruby 01-calcula-media.rb

Explicando o código


Primeiramente declaramos todas as variáveis que vamos usar no programa e inicializamos elas com valores padrão.

nome = ""
turma = ""
disciplina = ""
nota1 = 0.0
nota2 = 0.0
nota3 = 0.0
media = 0.0
aprovado = false

O que acontece aqui?

  • nome = "": Inicializa a variável nome como uma string vazia.
  • turma = "": Inicializa a variável turma como uma string vazia.
  • disciplina = "": Inicializa a variável disciplina como uma string vazia.
  • nota1, nota2, nota3 = 0.0: Inicializa as variáveis de notas como números de ponto flutuante (decimais) com valor 0.
  • media = 0.0: Inicializa a variável media como um número de ponto flutuante com valor 0.
  • aprovado = false: Inicializa a variável aprovado como um valor booleano falso.

Depois vem as entradas de dados (input) onde perguntamos as informações para a pessoa usuária e armazenamos nas variáveis correspondentes.

Perguntando o nome da pessoa:

print "Qual é seu nome? "
nome = gets.chomp

O que acontece aqui?

  • print "Qual é seu nome? ": Exibe a pergunta na tela sem pular para a próxima linha. PS. ainda deixamos um espaço depois da interrogação para a resposta da pessoa não ficar grudada na pergunta.
  • nome = gets.chomp: Lê a resposta digitada pela pessoa, remove a quebra de linha com chomp e armazena o valor na variável nome.

Perguntando a turma da pessoa:

print "Qual é a sua turma? "
turma = gets.chomp

Aqui acontece a mesma coisa que acontece na pergunta do nome, só que agora a resposta é armazenada na variável turma.

Perguntando a disciplina que está sendo avaliada:

print "Qual disciplina está sendo avaliada? "
disciplina = gets.chomp

Aqui acontece o mesmo que acontece nas perguntas anteriores, só que agora a resposta é armazenada na variável disciplina.

Conversão segura de tipo


Depois passamos a perguntar as notas e a executar um processo chamado conversão segura de tipo. Isso porque o gets.chomp sempre retorna uma string (texto), e como as notas são números decimais, precisamos converter essa string para o tipo numérico.

Mesmo que a pessoa digite:

7.5

O Ruby vai entender isso como:

"7.5" (texto)

E texto não pode ser somado nem dividido, então precisamos converter esse texto para um número de verdade.

Poderiamos usar o to_f para fazer essa conversão, como foi mostrado no tópico de conversão entre tipos de dados no artigo Introdução à Programação: Tipos de Dados - Parte II? Sim, poderiamos.

.to_f é uma forma rápida de converter um texto para float (número decimal), assim como existem o .to_i para converter para inteiro. Mas, to_f não é a melhor opção para esse caso. Ele tem um comportamento perigoso quando a pessoa digita algo errado.

Imagine que a pessoa digita texto ao invés de número, como por exemplo:

"abc".to_f   # vira 0.0
"banana".to_f # vira 0.0
"dez".to_f    # vira 0.0

Ou seja, to_f não gera um erro quando a conversão falha porque a pessoa digitou algo inválido, ele simplesmente converte tudo que é inválido para 0.0.

Se a pessoa digitar errado um texto, o programa não vai avisar, o programa não vai ser interrompido, ele vai continuar funcionando normalmente, mas a média calculada pode ficar errada porque uma ou mais notas podem ter sido convertidas para 0.0.

Continuando a explicação do código


Para evitar esse problema, usamos a função Float() para fazer a conversão segura de tipo. Ela tenta converter o texto para número decimal, mas se a conversão falhar (porque a pessoa digitou algo inválido), ela gera um erro e interrompe o programa, avisando que algo está errado.

Por isso perguntamos as notas assim:

print "Digite a primeira nota: "
nota1 = Float(gets.chomp)

print "Digite a segunda nota: "
nota2 = Float(gets.chomp)

print "Digite a terceira nota: "
nota3 = Float(gets.chomp)

Mostramos a pergunta com o print, lemos a resposta com o gets.chomp e convertemos para número decimal com o Float(), armazenando o resultado nas variáveis nota1, nota2 e nota3.

Depois calculamos a média das notas:

media = (nota1 + nota2 + nota3) / 3

Aqui somamos as três notas e dividimos por 3 para obter a média, armazenando o resultado na variável media.

Como na Matemática, a média é a soma dos valores dividida pela quantidade de valores e usamos parênteses para garantir que a soma seja feita antes da divisão.

Tudo em ponto flutuante (números decimais).

Depois verificamos se a pessoa foi aprovada ou reprovada:

aprovado = media >= 7.0

Fazemos uma comparação para verificar se a média é maior ou igual a 7.0. O resultado dessa comparação (verdadeiro ou falso) é armazenado na variável aprovado.

Por fim exibimos a mensagem personalizada com todas as informações:

puts "Olá, #{nome} da turma #{turma}!"

Aqui #{nome} e #{turma} são interpolação de strings (ou seja, inserir o valor da variável dentro do texto) e nesse caso exibimos o nome e a turma da pessoa.

puts exibe a mensagem na tela e pula para a próxima linha.

Depois exibimos a média final da disciplina com arredondamento para 2 casas decimais:

puts "A média final da disciplina #{disciplina} é #{media.round(2)}."

media.round(2) arredonda o valor da média para 2 casas decimais antes de exibir. Lembrando que round() é um método que pode ser usado em números para arredondar o valor. O número dentro dos parênteses indica quantas casas decimais queremos manter.

Finalmente exibimos a situação de aprovação ou reprovação:

puts "Sua situação é: #{aprovado ? 'Aprovado' : 'Reprovado'}."

O que isso significa?

Aqui estamos usando um operador ternário para decidir o que exibir com base no valor da variável aprovado.

Ou seja, se aprovado for verdadeiro (true), a mensagem exibida será “Aprovado”. Se for falso (false), a mensagem exibida será “Reprovado”.

#{aprovado ? 'Aprovado' : 'Reprovado'} quer dizer: “Se a variável aprovado for verdadeira, exiba ‘Aprovado’, caso contrário, exiba ‘Reprovado’.”

O ? é um operador ternário. Ternário significa que ele trabalha com três partes: uma condição, o que fazer se a condição for verdadeira, e o que fazer se a condição for falsa.

Observe bem:

#{aprovado ? 'Aprovado' : 'Reprovado'}

Vamos parte por parte:

  • aprovado: é a condição que estamos verificando (se a pessoa foi aprovada ou não).
  • ?: indica o começo do operador ternário que é usado para fazer uma escolha baseada na condição (se aprovado for verdadeiro).
  • 'Aprovado': é o valor que será retornado se a condição for verdadeira.
  • :: separa o valor verdadeiro do valor falso.
  • 'Reprovado': é o valor que será retornado se a condição for falsa.
  • O resultado final é inserido na string que será exibida pelo puts.

Então se a pessoa tiver uma média maior ou igual a 7.0, a variável aprovado será verdadeira, e a mensagem exibida será “Aprovado”. Caso contrário, a mensagem exibida será “Reprovado”.

É este o fluxo completo do programa, desde a entrada de dados até a saída de informações, passando pelo processamento dos dados (cálculo da média e verificação de aprovação).

Programa em execução


Aqui está um exemplo de como o programa se comporta quando executado:

parallels@ubuntu:~/curso-ruby$ ruby 01-calcula-media.rb
Qual é seu nome? Louise
Qual é a sua turma? 7º B
Qual disciplina está sendo avaliada? Matemática
Digite a primeira nota: 5.3
Digite a segunda nota: 2.6
Digite a terceira nota: 8.1
Olá, Louise da turma 7º B!
A média final da disciplina Matemática é 5.33.
Sua situação é: Reprovado.
parallels@ubuntu:~/curso-ruby$ 

Agora vamos inserir uma nota errada só para ver a conversão segura de tipo em ação:

parallels@ubuntu:~/curso-ruby$ ruby 01-calcula-media.rb
Qual é seu nome? Louise
Qual é a sua turma? 7º B
Qual disciplina está sendo avaliada? Matemática
Digite a primeira nota: 9.7
Digite a segunda nota: 2.1
Digite a terceira nota: abdcefg
<internal:kernel>:214:in `Float': invalid value for Float(): "abdcefg" (ArgumentError)
	from 01-calcula-media.rb:26:in `<main>'
parallels@ubuntu:~/curso-ruby$ 

Aqui a pessoa digitou abdcefg como terceira nota, que não é um número válido. O programa gerou um erro e foi interrompido, avisando que o valor é inválido para conversão para Float. Haverá um momento em nossa série de artigos onde aprenderemos a lidar melhor com erros, mas por enquanto é importante entender que esse comportamento é desejado para evitar cálculos errados.

Agora vamos testar a condição de aprovação:

parallels@ubuntu:~/curso-ruby$ ruby 01-calcula-media.rb
Qual é seu nome? Louise
Qual é a sua turma? 7º B
Qual disciplina está sendo avaliada? Matemática
Digite a primeira nota: 7.5
Digite a segunda nota: 8.1
Digite a terceira nota: 9.4
Olá, Louise da turma 7º B!
A média final da disciplina Matemática é 8.33.
Sua situação é: Aprovado.
parallels@ubuntu:~/curso-ruby$ 

Aqui a pessoa teve uma média maior que 7.0, então a mensagem exibida indica que ela foi aprovada.

Resumo


Neste artigo aprendemos sobre os conceitos de entrada e saída de dados em Ruby, entendendo como o programa interage com o usuário através dos comandos puts, print e gets.

Também exploramos os streams padrão do Ruby: STDIN, STDOUT e STDERR, que são os canais pelos quais o programa recebe e envia informações.

Além disso, criamos um programa completo que pergunta informações ao usuário, calcula a média de notas e exibe uma mensagem personalizada com o resultado, aplicando conceitos como interpolação de strings, conversão segura de tipos e operadores ternários.

Claro que muitos desses conceitos serão revisitados e aprofundados em artigos futuros, mas o importante é conhecer o básico para começar a criar programas.

Você deve ter observado que foram apresentados conceitos que não foram falados nos artigos anteriores, como o operador ternário (? e :) e a conversão segura de tipos com a função Float(). Eles foram apresentados somente no código na hora da construção do programa.

Por que isso foi feito assim? Não teria sido melhor seguir pelo caminho conhecido e usar recursos como o to_f por exemplo? Não seria melhor, mas seria confortável e são coisas diferentes.

Em programação é muito comum as pessoas seguirem caminhos lineares (linear é quando tudo é apresentado de forma sequencial, um passo após o outro, sem grandes saltos ou desvios). Isso gera uma sensação de segurança, conforto e controle, a pessoa sente que está dominando o assunto.

No geral isso é muito bom, principalmente se o curso ou tutorial for pago, porque não gera frustrações e a pessoa sente que está evoluindo. Só que no longo prazo isso tem um efeito negativo do qual eu fui e sou vítima: o medo de errar.

Seguimos tantos caminhos lineares que quando nos deparamos com algo novo, diferente ou que foge do padrão, sentimos medo de errar, medo de não entender, medo de não conseguir seguir a trilha. Isso é uma verdadeira porcaria, porque programação é sobre resolver problemas, e para resolver problemas precisamos explorar o desconhecido, precisamos errar, precisamos experimentar.

Eu acredito que ninguém aprende a programar conhecendo tudo primeiro para depois tentar fazer algo. Aprendemos a programar fazendo, errando, tentando de novo, explorando o desconhecido.

É por isso que eu escolhi apresentar alguns conceitos novos no meio do caminho, para que você comece a se acostumar com a ideia de que programação é sobre explorar o desconhecido, é sobre errar e tentar de novo.

A quebra da previsibilidade (linearidade) é dolorosa, desconfortável e dá vontade de desistir, eu sei disso porque já passei por isso muitas vezes. Mas é um passo necessário para quem quer aprender a programar.

Quando aprendemos um idioma novo, não começamos decorando todas as regras gramaticais, conjugando todos os verbos e aprendendo todas as palavras. Começamos com o básico, erramos muito, falamos errado, escrevemos errado, mas aos poucos vamos pegando o jeito.

Como? PRATICANDO. Vamos praticando e conhecendo novas palavras e regras aos poucos, conforme precisamos delas.

É nisso que eu acredito, na prática, na experimentação, no erro como parte do processo de aprendizado. Então eu vou sim inserir coisas novas no meio do caminho, para que você comece a se acostumar com isso.

Nos próximos artigos vamos praticar ainda mais, adicionando novos conceitos, fazendo perguntas e criando programas cada vez mais interessantes.