LLuciano Posted August 20, 2023 at 05:16 PM Share Posted August 20, 2023 at 05:16 PM 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; } 1 Quote Link to comment Share on other sites More sharing options...
mauro_b Posted August 23, 2023 at 10:29 PM Share Posted August 23, 2023 at 10:29 PM 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; } 1 1 Quote Link to comment Share on other sites More sharing options...
LLuciano Posted August 24, 2023 at 05:22 PM Author Share Posted August 24, 2023 at 05:22 PM (edited) 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 August 24, 2023 at 07:11 PM by LLuciano Só agora consegui compilar o código. 2 Quote Link to comment Share on other sites More sharing options...
Devair Posted May 31, 2024 at 04:50 PM Share Posted May 31, 2024 at 04:50 PM 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; } Quote Link to comment Share on other sites More sharing options...
fredericopissarra Posted June 3, 2024 at 04:33 PM Share Posted June 3, 2024 at 04:33 PM (edited) 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 June 3, 2024 at 04:35 PM by fredericopissarra 1 1 Quote Link to comment Share on other sites More sharing options...
LLuciano Posted July 3, 2024 at 01:51 PM Author Share Posted July 3, 2024 at 01:51 PM 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; } 1 Quote Link to comment Share on other sites More sharing options...
fredericopissarra Posted July 3, 2024 at 02:34 PM Share Posted July 3, 2024 at 02:34 PM 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 1 Quote Link to comment Share on other sites More sharing options...
mauro_b Posted August 7, 2024 at 04:02 PM Share Posted August 7, 2024 at 04:02 PM (edited) 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 August 7, 2024 at 04:02 PM by mauro_b 2 Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.