Jump to content

Menu numérico sem erros.


LLuciano

Recommended Posts

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 to comment
Share on other 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 to comment
Share on other 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.

 

Edited by LLuciano
Só agora consegui compilar o código.
  • Agradecer 2
Link to comment
Share on other sites

  • 9 months later...

    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 to comment
Share on other 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.

Edited by fredericopissarra
  • Agradecer 1
  • Curtir 1
Link to comment
Share on other sites

  • 5 weeks later...

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;
}

 

  • Agradecer 1
Link to comment
Share on other 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

  • Agradecer 1
Link to comment
Share on other sites

  • 1 month later...

Bom dia, @LLuciano

ISSO É ERRO DO USUÁRIO E FALHA DO PROGRMADOR

while|scanf|printf

 

Interessante, porque este único “erro” é também uma falha do programador: falha o usuário por não entender a mensagem ou por não entender o programa; considere a má formatação dum campo uma limitação do programa, onde posso informar texto num campo numérico e com isso falhar. Essa falha configurará uma falha do progrmador, somente se não for tratada. Em tese, trata-se de falhas a partir das especificações e recursos da linguagem. Neste caso, imagine ser um exercício, e seu o apontamento alencou o estudo de 2 funções (scanf e printf) e uma declaração de repetição (while), com esses recursos não é possível limitar as teclas, aquilo que o usuário consegue teclar ou entrar numa linha, essas funções não têm poder para essas coisas. Logo, ele (o usuário) é livre para teclar texto num campo numérico, incluindo espaço. Para tratar essa falha ou impedir um erro, defina o terminador de campo, alerta para falhas, bem como valor padrão ou, até mesmo, o caso de solicitar que entre novamente com a linha válida.

Sugere um exercício de estrutura de repetição comumente nomeado de VALIDAÇÂO DE CAMPO.

 

Edited by mauro_b
  • Agradecer 2
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...