Lendo e Escrevendo Strings em C

Que 99,99% dos aplicativos em C, ou de qualquer outra linguagem, usam strings e caracteres para mostrar textos, nós já convencemos você.

Mas só mostrar não adianta muita coisa, geralmente é preciso receber strings do usuário.
Afinal, quem nunca forneceu o nome pra ficar no ranking daquele jogo, data e local de nascimento, nome dos pais e outros tipos de texto?

Nesse tutorial sobre strings em C, vamos ensinar como receber textos do usuário.

Strings em C: %s

Antes de iniciar nosso estudo das funções que recebem e lêem strings, vamos apresentar o %s.
Você deve se lembrar que usamos %d para representar números inteiros, %f para números decimais e %c para caracteres.

Para strings, usamos o %s.


Como ler strings com a função scanf()

Assim como os números e caracteres, também podemos usar a nossa tão conhecida função scanf para receber uma string do usuário.

Há um porém com a leitura de strings e um problema com a função scanf para strings.

O porém é o seguinte: lembra que repetimos, exaustivamente, que o nome de um vetor nada mais é que o endereço de memória do primeiro elemento do vetor?
Lembra que dissemos também no tutorial de C passado, sobre a definição de strings, que strings são um vetor de caracteres com um caractere delimitador \0 no final?
E por fim, lembra que, para armazenar dado em uma variável, na função scanf() nós sempre passamos o endereço da variável ( &variável)?

Se você conseguiu ver onde chegamos, notará que para receber uma string do usuário através da função scanf(), não é necessário colocar o operador &, pois o nome da string em si já é um endereço de memória.

A sintaxe para receber uma string por meio da scanf() é:
scanf(“%s”, nome_da_string);

Exemplo: Programa que pede o nome e sobrenome em C
Crie um aplicativo em C que peça ao usuário seu nome, armazene em uma String, peça o sobrenome, armazene em outra string e exiba o nome do usuário de maneira formal ( Sobrenome, Nome).

Criamos duas strings, optamos por colocar 20 caracteres em cada, como temos que ter o caractere delimitador, então declaramos o vetor de caracteres com 21 elementos cada, um pro nome e outro pro sobrenome.

Depois pedimos o nome ao usuário, armazenando na variável ‘nome’, e fazemos o mesmo na variável ‘sobrenome’.
Em seguida, para exibir a string completa, basta usarmos o símbolo %s , da mesma maneira que vínhamos fazendo com %d, %f e %c na printf()

Veja como ficou o código de nosso programa em C:

#include <stdio.h>
// Curso C Progressivo: www.cprogessivo.net
// O melhor curso de C! Online e gratuito !
// Apostila online, tutorial completo sobre
// a linguagem de programação C !
 
int main()
{
    char nome[21], sobrenome[21];
 
    printf("Primeiro nome: ");
    scanf("%s", nome);
 
    printf("Ultimo sobrenome: ");
    scanf("%s", sobrenome);
 
    printf("Ola senhor %s, %s. Bem-vindo ao curso C Progressivo.\n", sobrenome, nome);
    return 0;
}


Vamos agora apontar o problema do uso da função scanf() receber dados do usuário.
Você deve ter digitado apenas um nome e um sobrenome.

Pois bem, digite um nome composto no ‘nome’ ou no ‘sobrenome’. Ou seja, um nome com espaço em branco.

A scanf() vai simplesmente cortar seu nome composto. Essa função pega tudo até encontrar um espaço em branco, caractere new line \n, tab ou ENTER.
Para corrigir isso, o C tem uma função especial e bem mais simples para receber strings do usuário, a função gets().


Como receber strings do usuário com a função gets()

gets vem de get string. Como sempre, algo bem óbvio e de fácil memorização.

Para usar a função gets(), é bem simples, basta passar uma string como argumento.
A sintaxe é:
gets( nome_da_string );

Mais uma vez, nunca use & quando for armazenar uma string. Isso não é necessário, pois string é um vetor, e o nome do vetor já é um endereço, e um endereço é o operador & seguido do nome da variável.

Como exibir strings com a função puts()  printf()

Já que mostramos como obter, facilmente, uma string do usuário a partir da função gets(), nada mais justo do que existir uma função que exibe uma string.

Essa função existe e é a puts() (de put string), e sua sintaxe é idêntica a da gets():
puts( nome_da_string_a_ser_exibida);

A nossa velha e conhecida printf também serve para exibir strings.
Como dissemos, as strings são caracterizadas por %s.
Então, para exibir uma string "str" com o printf, fazemos:
printf("Minha string: %s", str);

Exemplo de código: Programa em C que pede os dados cadastrais completos
Crie um aplicativo em C que peça o nome do usuário, sua idade e data de nascimento.

Esse aplicativo é bem simples, e certamente você fará sem maiores problemas.
Porém, existe uma pequena casca de banana nele, que colocamos de propósito.

Vimos que a função scanf() pega tudo até aparecer o primeiro espaço em branco, e pára antes dele.
Já a gets() não, ela pega tudo até aparecer uma new line \n, inclusive nada. Ou seja, se você der um ENTER, a gets() vai armazenar esse enter na string.

Note que após digitar o inteiro correspondente a idade, você dá um enter.
Esse número vai pra variável ‘idade’, mas e o ENTER, pra onde vai?
Vai pro buffer.

O problema é que a função gets() vai pegar o que está armazenado nesse buffer e vai armazenar  o que estiver lá na string de data de nascimento!
E como evitar isso? Ora, é só apagar esse ENTER que está no buffer, usando o fflush(stdin) caso use Windows, ou __fpurge(stdin) caso seja abençoado e use Linux.

Então nosso código em C fica:

#include <stdio.h>
// Curso C Progressivo: www.cprogessivo.net
// O melhor curso de C! Online e gratuito !
// Apostila online, tutorial completo sobre
// a linguagem de programação C !
 
int main(void)
{ char nome[31], sobrenome[31], nascimento[11]; int idade; printf("Nome: "); gets(nome); printf("Sobrenome: "); gets(sobrenome); printf("Idade: "); scanf("%d", &idade); fflush(stdin); printf("Data de nascimento: "); gets(nascimento); printf("\nNome completo: %s %s\n", nome, sobrenome); printf("Idade: %d\n", idade); printf("Data de nascimento: "); puts(nascimento);

    return 0;
}

Problemas com a gets() - A função fgets()

Como C é uma linguagem de programação de baixo nível, algumas coisas são um pouco 'chatinhas' de serem usadas, pois se exigirá um rigor muito grande, que é pouco visto em outras linguagens de programação.

Um exemplo disso, que já mostramos neste mesmo artigo de nossa apostila, foi o da scanf(), que só lê até o primeiro espaço, pois isso é a característica padrão da função.
É característica padrão, e não obrigatória. Ou seja, embora poucos saibam, é possível alterar o funcionamento da scanf(). Por exemplo, se quisermos ler strings que tenham espaço, nós temos que dizer isso dentro da função.

Vamos dizer para a scanf() parar de pegar nossa string somente quando encontrar um caractere de NEW LINE (um enter). Para isso, usamos o operador: [^\n]
Logo, nosso código da scanf() para ler strings com espaços e armazenar na variável "str" é:
scanf ( "%[^\n]", str);

(lembre-se de limpar o buffer, usando __fpurge(stdin) ).

Podemos ainda limitar o tamanho de nossa string, basta colocar um numero inteiro ao lado do %, representando o número de caracteres máximo, o que é uma excelente prática, pois essa função pode ocasionar problemas na memória, caso você estoure os limites da string.
Por exemplo:
scanf ( "%256[^\n]", str);

A função gets() peca nesse quesito, de tamanho da string, pois podemos digitar mais caracteres do que a string alocou de memória, e "quebraríamos" o programa por conta de um overflow.
Algumas pessoas relatam problemas de usar ela em um ambiente Linux.

Uma solução para isso é usar a função fget(), que é mais segura.
Ela recebe três dados: a string que vai armazenar o que vai ser digitado ( no nosso caso é a variável "str"), o tamanho da string e de onde vai ler (ela pode ler de um arquivo de texto, por exemplo).
Para ler do teclado, usamos stdin.

Veja como ficaria 
fgets(str, 256, stdin);

Que função usar para ler strings, então? 

Se você está iniciando seus estudos e fazendo programas simples, apenas para aprender a linguagem C, pode usar a gets() ou scanf(), sem problemas.

Como dissemos, elas podem ocasionar problemas na memória, onde é fácil extrapolarmos o limite da memória, encerrando o programa por isso. Portanto, se quiser usar uma função de maneira segura, em programas profissionais, evite a gets(). Use fgets() ou scanf() sempre com litadores de new line e tamanho da string.

Para saber mais sobre assunto, leia:
http://linux.die.net/man/3/gets
http://stackoverflow.com/questions/3302255/c-scanf-vs-gets-vs-fgets
http://stackoverflow.com/questions/1252132/difference-between-scanf-and-fgets

22 comentários:

Raquel Bueno disse...

Porque apenas a data de nascimento possui o puts em seguida?

Apostila de C disse...

Raquel,

Foi apenas para ilustrar o uso da função puts.

Poderíamos ter usado, por exemplo:
printf("Data de nascimento: %s", nascimento);

Marcos Farias disse...

Como salvo em uma variavel um string composto, nome e sobrenome, no linux? A biblioteca conio.h nao existe, entao o comando gets() ou getch() nao funciona, e o scanf() captura somente o primeiro nome.

Apostila C Progressivo disse...

Olá Marcos,

Adicionamos mais 2 tópicos nesta aula de nossa apostila, visando tirar sua dúvida. Veja se consegue ter sucesso agora.

Anônimo disse...

Oi,

qual a diferença entre scanf("%[^\n]",str) e scanf(\n%[^\n],str) ???

Iosley Carlos disse...

Alguem sabe como ler um vetor de string ? sei q uma string em C é um vetor de char mas se eu quiser fazer um vetor de string como faço ? ex: ler 20 nomes. tenho q criar 20 variaveis para receber um vetor de char ou há uma outra forma ?
ajuda awe :D

Apostila C Progressivo disse...

Iosley,

Você precisa criar um vetor de string, mas strings são vetores, então você vai criar um vetor de vetores.

O nome disso é matriz ou vetor multidimensional.

Por exemplo, se quiser criar 20 strings, onde cada uma delas vai armazenar 40 chars, declare:
matriz[20][40]

Suas strings são matriz[0], matriz[1], ..., matriz[19].

Na nossa seção de Vetores falamos mais sobre matrizes.

Apostila C Progressivo disse...

Iosley,

Você precisa criar um vetor de string, mas strings são vetores, então você vai criar um vetor de vetores.

O nome disso é matriz ou vetor multidimensional.

Por exemplo, se quiser criar 20 strings, onde cada uma delas vai armazenar 40 chars, declare:
matriz[20][40]

Suas strings são matriz[0], matriz[1], ..., matriz[19].

Na nossa seção de Vetores falamos mais sobre matrizes.

Anônimo disse...

Fiz a seguinte alteração no seu código apenas para testar o fgets:
#include
// Curso C Progressivo: www.cprogessivo.net
// O melhor curso de C! Online e gratuito !
// Apostila online, tutorial completo sobre
// a linguagem de programação C !

int main(void)

{
char nome[31], sobrenome[31], nascimento[11];
int idade;

printf("Nome: ");
fgets(nome,31,stdin);

printf("Sobrenome: ");
fgets(sobrenome,31,stdin);

printf("Idade: ");
scanf("%d", &idade);

printf("Data de nascimento: ");
scanf(" %11[^\n]",nascimento);

printf("\nNome completo: %s %s\n", nome, sobrenome);
printf("Idade: %d\n", idade);
printf("Data de nascimento: "); puts(nascimento);
return 0;

}

porem, quando vai imprimir o nome, ele pula uma linha para mostrar o sobrenome, por que isso acontece?

Rodrigo disse...

Parabéns pelo site, vocês ajudam muito quem precisa!

Não consegui assimilar muito bem o scanf:
" scanf ( "%256[^\n]", str); "

Em relação ao 256, quando declaramos a variável, já não estamos dizendo o tamanho que ela pode ter?
ex: char str [20];

De novo, parabéns pelo site, vocês fizeram um ótimo trabalho!

Anderson Nobre disse...

No código enviado pelo anônimo acima, a quebra de linha ao imprimir o nome e o sobrenome ocorre pois a função fgets armazena também o '\n'. Nesse trecho de código está a correção através do uso do scanf e getchar, e através de uma função criada para remover o '\n'.

#include
void remove_barra_n(char *v)
{
while (*v!='\n')
{++v;}
*v= '\0';
}

int main(void)

{
char nome[31], sobrenome[31], nascimento[11];
int idade;

printf("Nome: ");
scanf("%s",nome); //a função scanf não armazena o '\n'
getchar(); /* essa função também pode ser usada para liberar o '\n' que esta em buffer nesse momento*/

printf("Sobrenome: ");
fgets(sobrenome,31,stdin);
remove_barra_n(sobrenome);// funçao que remove o '\n' da string

printf("Idade: ");
scanf("%d", &idade);

printf("Data de nascimento: ");
scanf(" %11[^\n]",nascimento);

printf("\nNome completo: %s %s\n", nome, sobrenome);
printf("Idade: %d\n", idade);
printf("Data de nascimento: "); puts(nascimento);
return 0;

}

Vadio Reis disse...

Boa noite!
Como faço para camuflar uma String que o usuário está digitando. Eu gostaria que a medida que o usuário fosse digitando (letra a letra) uma outra String pré-definida aparecesse...

Anônimo disse...

#include
#include
main()
{
float altura,homens,mulheres;
char sexo[21];
printf("Imforme sua altura: ");
scanf("%f", &altura);
fflush(stdin);
printf("Infome o seu sexo: ");
gets(sexo);
homens = (72.7*altura)-58;
mulheres = (62.1*altura)-44.7;
if (sexo == "masculino")
{
printf("Peso ideal para voce: %fkg",homens);
}
else
{
printf("Peso ideal para voce: %fkg",mulheres);
}
}

Como que eu faço para dizer para o programa q oq foi digitado foi a palavra "masculino" e com isso ele mostre o valor da variavel homens. se eu escrever femenino teria que aparecer a variavel mulheres.

Anônimo disse...

Acho que você deve usar Swich Case

Anônimo disse...

Muito obrigado irmão, sua explicação me ajudou muito em um trabalho da faculdade muito obrigado mesmo, agora vou seguir seu blog.

Anônimo disse...

Se colocar uma string com gets, fgets ou [^\n}, dentro de um FOR. E digitar uma string com espaço, o valor após o espaço esta sendo salvo na segunda varialvel. Gostaria de saber, o que fazer pra resolver isso.

Caroline Nunes disse...

Uma dúvida: como armazenar em um espaço de memória um "@"?
Não sei se utiliza char ou string, por favor me ajudem (:

efrain lopes disse...

Show muito bem explicado ✰ ✰ ✰ ✰ ✰

efrain lopes disse...

✰ ✰ ✰ ✰ ✰ Muito Bem explicado parabéns!

Anônimo disse...

qual instrução que solicita a digitação de um elemento especifico de uma string?
Ex: uma string de 10 caracteres, onde o programa pede ao operador que digite um elemento para a posição 6.

Jorge Luis disse...

Olá! Boa Noite. Me ajude neste problema, pois não estou conseguindo. Desde já agradeço a quem puder me ajudar.
É o seguinte, estou tentando ler DOIS CARACTER, mas sempre dá conflito em um deles. O problema proposto é esse abaixo.
Faça um programa em C++ que receba a idade, o peso, a altura, a cor dos olhos (A – azul, P – preto, V – verde e C – castanho) e a cor dos cabelos (P – preto, C – castanho, L – louro e R – ruivo) de vinte pessoas, e que calcule e mostre:

• a quantidade de pessoas com idade superior a 50 anos e peso inferior a 60 quilos;
• a média das idades das pessoas com altura inferior a 1,5 metro;
• a percentagem de pessoas com olhos azuis entre todas as pessoas analisadas;
• a quantidade de pessoas ruivas e que não possuem olhos azuis.

ESTOU TENTANDO FAZER DESTA MANEIRA:
#include
#include
#include
#include
main(){
int i=0, idade=0, id_peso=0, cont=0, azul=0, cruivo=0, res=0, md=0;
float peso=0, alt=0, perc=0;
char olho, c_cab;

for(i=0;i<3;i++){
printf("\nCOR DOS OLHOS. [A] p/ azul, [P] p/ preto, [V] p/ verde, [C] p/ castanho: ");
scanf("%s", &olho);
printf("COR DOS CABELOS? [P] p/ preto, [C] p/ castanho, [L] p/ louro, [R] p/ ruivo: ");
scanf("%s", &c_cab);
printf("Digite a altura: "); scanf("%f",&alt);
printf("Digite a idade: "); scanf("%d",&idade);
printf("Digite o peso: "); scanf("%f",&peso);
olho = tolower(olho);
c_cab = tolower(c_cab);
if(olho == 'a')
azul++;
else if(c_cab == 'r' && c_olho != 'a')
c_ruivo++;
else if(alt<1.50){
md += idade;
cont++;
}
else if(idade>50 && peso<60)
id_peso++;
else
printf("Informacao incorreta\n");
}
perc = o_azul * 0.03;

printf("\n\nA qtde de pessoas com idade superior a 50, e peso inferior a 60 KG: %d\n",id_peso);
printf("A media das idades das pessoas com altura inferior a 1.50 m: %.2f\n", (float)md/cont);
printf("A percentagem de pessoas com olhos AZUIS entre todas as pessoas analisadas: %.2f\n", perc);
printf("A quantidade de pessoas RUIVAS e que NAO possuem OLHOSAZUIS: %d\n",c_ruivo);
}

NÃO ESTÁ CONTANDO OS "OLHOS AZUIS", E NEM OS "CABELOS RUIVOS". MAS QUANDO EU TIRO ESSA LINHA DO CÓDIGO CONTA NORMAL OS OLHOS AZUIS "printf("COR DOS CABELOS? [P] p/ preto, [C] p/ castanho, [L] p/ louro, [R] p/ ruivo: ");
scanf("%s", &c_cab);"
O resto do código está OK. SÓ TÁ DANDO PROBLEMA NESSA PARTE. ME AJUDEM POR FAVOR.

Unknown disse...

Vc tem que usar Matriz (Vetor de vetor)
ex.:
int main(void)
{
char nome [20 /*quantidade de nomes*/][30 /*tamanho máximo dos nomes*/];
}
se usa (gets(nome[índice]);)
procura na net q fica mais fácil

Gostou desse tutorial de C?
Sabia que o acervo do portal C Progressivo é o mesmo, ou maior que, de um livro ou curso presencial?
E o melhor: totalmente gratuito.

Mas para nosso projeto se manter é preciso divulgação.
Para isso, basta curtir nossa página no Facebook e/ou clicar no botão +1 do Google.
Contamos e precisamos de seu apoio.