Ir para conteúdo

Menu numérico sem erros.


LLuciano

Posts Recomendados

Olá pessoal!

Estou estudando C sozinho. Em vários exercícios tem a necessidade de criar uma espécie de menu com opções. Mas, considerando o que eu aprendi até agora, é muito fácil de dar um erro caso o usuário digite algo errado, como por exemplo uma letra quando eu esperava um inteiro. Isso me dá uma angustia, ficar fazendo exercício cujo o programa pode ser facilmente quebrado.

Eu procurei uma função na biblioteca padrão para isso mas não encontrei, então tentei fazer eu mesmo uma solução.  Eis as dúvidas:

1 - Existe alguma função que já implementa isso?

2 - O código que desenvolvi está funcionando... mas parece que estou com a mente viciada.Fico com a impressão de que poderia ser mais simples mas não consigo fazer melhor.

Alguém tem alguma sugestão? (Fiz somente considerando opções numéricas, não alfabéticas, ou com palavras)

Eis o código:

#include <stdio.h>
#include <ctype.h>

int menuNumberCheck (void);

int main(void){
	int choice = 0;
	do {
		printf("%s", "Digite um número de 0 a 10 ou digite 11 para finalizar: ");
		choice = menuNumberCheck();

		if (choice < 0 || choice > 11){
			puts("Opção inválida");
		} else printf("Você escolheu a opção %d\n", choice);
		
	} while (choice != 11);

	puts("Programa finalizado");
	return 0;
}

int menuNumberCheck (void){

	int check = 1;
	char c;
	int number = 0;
	int loopcount = 0;

	while ((c = getchar()) != '\n'){
		
		if (isdigit(c) && check == 1){
			//check multpiplication overflow:
			//se verdadeiro é que vai ocorrer overflow e retornar erro.
			//multiplica o dígito pode 10 para encontrar a posição (unidade, dezena e etc);
			if (__builtin_smul_overflow(number, 10, &number)) {
				puts("Overflow");
				check = 0;

			//check sum overflow
			//soma o digito encontrado na unidade
			//se verdadeido é que vai ocorrer overflow e retornar erro.
			// c-48 para encontrar o número na tabela ANSCI-I
			} else if (__builtin_sadd_overflow(number, c-48, &number)) {
				puts("Overflow");
				check = 0;
			} else loopcount++;

		} else check = 0;//falso se encontrar algum caracter não numérico.
	}
	if (check == 0 || loopcount == 0){
		return -1;
	} else return number;
}

 

 

  • Agradecer 1
Link para o comentário
Compartilhar em outros sites

Olá, @LLucianoexperimente uso com a função scanf da biblioteca padrão, após leitura de sua definição reescreva teu código. 

Um exemplo C'11

#include <stdio.h>

int main()
{
    int choice = -1;
	do {
		printf("%s", "Digite um número de 0 a 10 ou digite 11 para finalizar: ");
		int decimal_input = scanf("%d", &choice);

		if (choice < 0 || choice > 11){
			puts("Opção inválida");
			if (decimal_input == NULL) scanf("%*s"); /*se entra com caractere não decimal, consuma sua string*/
		} else printf("Você escolheu a opção %d\n", choice);
		
	} while (choice != 11);

	puts("Programa finalizado");
	return 0;
}

 

  • Curtir 1
  • Haha 1
Link para o comentário
Compartilhar em outros sites

Em 23/08/2023 em 19:29, mauro_b disse:

Olá, @LLucianoexperimente uso com a função scanf da biblioteca padrão, após leitura de sua definição reescreva teu código. 

Um exemplo C'11

#include <stdio.h>

int main()
{
    int choice = -1;
	do {
		printf("%s", "Digite um número de 0 a 10 ou digite 11 para finalizar: ");
		int decimal_input = scanf("%d", &choice);

		if (choice < 0 || choice > 11){
			puts("Opção inválida");
			if (decimal_input == NULL) scanf("%*s"); /*se entra com caractere não decimal, consuma sua string*/
		} else printf("Você escolheu a opção %d\n", choice);
		
	} while (choice != 11);

	puts("Programa finalizado");
	return 0;
}

 

Nossa! Valeu!

Era só ter estudado o scanf decentemente! Tinha que ser mais simples que eu estava fazendo.

 

Editado por LLuciano
Só agora consegui compilar o código.
  • Agradecer 2
Link para o comentário
Compartilhar em outros sites

  • 9 meses depois...

    essa questão  de ler o teclado é bem complicado mesmo e muitas tentativas de implementar funções que resolvam já existem pela internet , e resolvem a maioria dos casos , e todas as tentativas são válidas , porém algumas vezes  surge algum erro ,  e uma solução que encontrei em alguns sites foi essa , que usa uma string da linguagem C , para pegar o que for digitado pelo usuário , pois em uma "string" pode se inserir qualquer caractere , e assim não haverá erro na leitura , e não vai quebrar o código , e depois de pegar os dados usar a função "atoi" da da biblioteca "<stdlib.h>" da linguagem C para converter a string em um valor decimal normal , e assim se houver caracteres inválidos no início , o valor retornado será zero mesmo , e se o início for um valor válido esse valor é que será retornado ,  e que sempre será maior ou igual a zero ,  e funcionou bem , . . . 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
    int choice = -1;/// escolha
    char str[20] = "";
    char c = 'w';
    do
    {
        printf("%s", "Digite um número de 0 a 10 ou digite 11 para finalizar: ");
        scanf("%s",  str);
        choice = atoi( str );
        printf("val Choice = %d\n",choice);
        if ( /*choice < 0 || */  choice > 11)/// nunca serAh menor Q zero
        {
          while(c != '\n' && c != EOF)/// limpar o buffer
            c = fgetc(stdin);
            puts("Opção inválida");
        }
        else 
          printf("Você escolheu a opção %d\n", choice);

    }
    while (choice != 11);

    puts("Programa finalizado");
    return 0;
}

 

Link para o comentário
Compartilhar em outros sites

Well... UMA opção é usar o scanf(), como o povo te disse, mas há um detalhe: A função usa o stream stdin e os buffers de streams de entrada não podem ser "descartados" com fflush(). A função scanf() "varre" (scans) o stream em busca do formato informado. Uma vez que o formato foi completado, os dados são colocados nos ponteiros informados e o restante do buffer é mantido no stream. Por exemplo, isso pode ser problemático:

int op;

do {
  scanf( "%c", &op );
} while ( op != '0' );

O loop acima será executado DUAS vezes se op for igual a zero na primeira "varrida"... Ele será o valor digitado na primeira vez e '\n' na segunda (porque scanf deixará o '\n' no buffer do stream ao converter o caracter inicial.

OBS: O formato %c exige um ponteiro para int, não para char!

Geralmente o pessoal contorna isso adicionando um espaço no início do formato: scanf( " %c", &op ). Isso porque scanf() usa a função isspace() para ignorar "espaços", nesse caso.

OUTRA solução mais "esperta" é usar a função fgets(), com um buffer de tamanho máximo conhecido:

char buffer[11]; // 11 chars suporta valores de 9 algarismos + 1 '\n' e mais o '\0' final.
char *p
...
fgets( buffer, sizeof buffer, stdin );
buffer[sizeof buffer - 1] = '\0';  // para ter certeza que sempre tenhamos uma string.
p = buffer + strspn( buffer, " \t\n\r" ); // ignora espaços, tabs, '\n' ou '\r' inciais, se houverem.
op = atoi( p );  // lembrando que op será zero se a string não puder ser convertida.

Com isso você garante que o buffer estará sempre vazio na próxima leitura (desde que buffer seja grande o suficiente para caber toda a "linha" lida) - omiti o teste do retorno de fgets() ai em cima por simplicidade - é bom testar se ele retorna ou não NULL.

atoi() pode ser substituída por strtol() para maior controle de erros.

Editado por fredericopissarra
  • Agradecer 1
  • Curtir 1
Link para o comentário
Compartilhar em outros sites

  • 5 semanas depois...

Olha só que legal essa função que achei no livro C Primer Plus, funcionou muito bem.

O único "erro" que achei é quando o usuário digita um número válido seguido de espaço e outra coisa (se for outro número o loop conta como várias escolhas).

 

long get_long(void)
{
  long input;
  char ch;

  while (scanf("%ld", &input) != 1)
  {
    while ((ch = getchar()) != '\n')
      putchar(ch); // dispose of bad input
    printf(" is not an integer.\nPlease enter an ");
    printf("integer value, such as 25, -178, or 3: ");
  }
  return input;
}

 

Link para o comentário
Compartilhar em outros sites

Um jeito mais fácil que ainda tem um problema:

#include <stdio.h>

int main( void )
{
  char buffer[128];  // um tamanho grande, arbitrário.

  puts( "Digite alguns números e <enter>" );

  while ( fgets( buffer, sizeof buffer, stdin ) )
  {
    // Atenção: `long int`, no Windows, é o mesmo que `int`.
    long long int n;

    if ( sscanf( buffer, " %lld", &n ) != 1 )
      break;

    printf( "read: %lld\n", n );
  }
}

Uma versão melhor:
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main( void )
{
  char buffer[128];  // um tamanho grande, arbitrário.

  puts( "Digite alguns números e <enter>" );

  while ( fgets( buffer, sizeof buffer, stdin ) )
  {
    // Atenção: `long int`, no Windows, é o mesmo que `int`.
    long long int n;
    char *p;

    p = buffer + strspn( buffer, " \f\n\r\t\v" );

    errno = 0;
    n = strtoll( p, NULL, 10 );
    if ( errno )
    {
      fputs( "ERRO convertendo string.\n", stderr );
      continue;
    }

    printf( "read: %lld\n", n );
  }
}

[]s
Fred

Link para o comentário
Compartilhar em outros sites

Participe da conversa

Você pode postar agora e se cadastrar mais tarde. Se você tem uma conta, faça o login para postar com sua conta.

Visitante
Responder

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emojis são permitidos.

×   Seu link foi automaticamente incorporado.   Mostrar como link

×   Seu conteúdo anterior foi restaurado.   Limpar o editor

×   Não é possível colar imagens diretamente. Carregar ou inserir imagens do URL.

  • Quem Está Navegando   0 membros estão online

    • Nenhum usuário registrado visualizando esta página.
×
×
  • Criar Novo...