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.


%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

15 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...

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 (:

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.