Ir ao conteúdo
  • Cadastre-se

C Limpar buffer após varias inserções


Posts recomendados

estou fazendo um código em C, e em algumas partes do código eu quero que o usuário, aperte ENTER pro programa continuar a rodar normalmente, porém eu fiz testes, e se eu apertar ENTER varias vezes, os próximos "APERTE ENTER PRA CONTINUAR" entram automaticamente mesmo com o uso do fflush(stdin);/__fpurge(stdin); o programa continua inserindo os ENTER que eu apertei repetidamente nas próximas partes do código, há um jeito de fazer com que esses ENTER "sumam" mesmo que seja apertado varias vezes antes desse getch/getchar/scanf? tentei de varias maneira e nenhuma até agora deu certo,
há uma maneira de limpar o buffer completamente a ponto de eliminar qualquer tecla apertada, incluindo o ENTER, pra quando ele chegar no próximo getchar(); eu ter que apertar ENTER novamente pra ele continuar o programa?
Esse eh o repositório do código no GIThub se quiserem dar uma olhada pra entender melhor. https://github.com/felps18/jogo.git

Link para o comentário
Compartilhar em outros sites

@felipe pereira forte 

2 horas atrás, felipe pereira forte disse:

há um jeito de fazer com que esses ENTER "sumam" mesmo que seja apertado varias vezes antes desse getch/getchar/scanf? tentei de varias maneira e nenhuma até agora deu certo

Acho que "sumam" não é a palavra certa.

O que especificamente você está usando para aguardar uma tecla? system("pause")? getchar()? _getch()?

Tentou limpar o buffer exclusivamente antes de aguardar uma tecla a ser pressionada pelo usuário?

 

2 horas atrás, felipe pereira forte disse:

há uma maneira de limpar o buffer completamente a ponto de eliminar qualquer tecla apertada, incluindo o ENTER

Há várias maneiras de limpar o buffer...

scanf("%*c");
//ou
scanf("%*[^\n]");
//ou
int ch;
do {
    ch = fgetc(stdin)
} while (ch != EOF && ch != '\n');
//ou
int ch;
while((ch = getchar()) != EOF && ch != '\n');

 

Link para o comentário
Compartilhar em outros sites

No Windows o oficial é uma linha só:

FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); // em windows.h

Você pode dar uma olhada no buffer antes também e ver o que tem para ser lido usando PeekConsoleInput() como descrito em https://docs.microsoft.com/en-us/windows/console/peekconsoleinput. Essa rotina permite que você veja o que tem pra ser lido, sem ler. E aí pode agir de acordo com a sua necessidade.


 

3 horas atrás, felipe pereira forte disse:

fflush(stdin);/__fpurge(stdin);

 

Não use essas coisas para streams de entrada. Mesmo que funcionem em algum sistema contraria a definição de flush: o comportamento de fflush() para fluxos de entrada é indefinido. 

 

setbuf() também não é pra isso. Sugiro esquecer.

 

3 horas atrás, felipe pereira forte disse:

e se eu apertar ENTER varias vezes, os próximos "APERTE ENTER PRA CONTINUAR" entram automaticamente

 

Não é que eles entram automaticamente. Apenas eles não saem automaticamene. Alguém digitou. A obrigação do sistema é manter lá até alguém ler. É a noção de input stream. 

 

Postei algo sobre isso e um programa de exemplo ontem, e pode ser interessante dar uma olhada em 

 

 

E lá também descrevo uma maneira oficial segura de tratar isso.

 

Se está controlando um jogo a partir do teclado em Windows, e precisa usar a console mesmo, recomendo usar apenas PeekConsoleInput(). É mais simples. E não precisa parar o jogo pra ler, que é o grande inconveninente. No loop da interface você apenas usa peek() e volta pra animação. Se tem algo pra ler você vê o que é, trata e descarta, SEM PARAR O PROGRAMA.

 

 

Link para o comentário
Compartilhar em outros sites

4 horas atrás, Lucca Rodrigues disse:

@Lucca Rodrigues

Citação

 

O que especificamente você está usando para aguardar uma tecla? system("pause")? getchar()? _getch()?

Tentou limpar o buffer exclusivamente antes de aguardar uma tecla a ser pressionada pelo usuário?

 

 

estou usando getch(); e se eu apertar o enter 5x após esse getch(); o próximo getch(); ja aceita o enter e nao para nele


e o codigo que você mandou ja utilizei, nao funcionou pra tratar o enter e excluir

 

adicionado 7 minutos depois
2 horas atrás, arfneto disse:

FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); // em windows.h
Citação

Você pode dar uma olhada no buffer antes também e ver o que tem para ser lido usando PeekConsoleInput() como descrito em https://docs.microsoft.com/en-us/windows/console/peekconsoleinput. Essa rotina permite que você veja o que tem pra ser lido, sem ler. E aí pode agir de acordo com a sua necessidade.

pode me explicar melhor como funciona e algum codigo limpando o buffer com ele?

 

 

Citação

Se está controlando um jogo a partir do teclado em Windows, e precisa usar a console mesmo, recomendo usar apenas PeekConsoleInput(). É mais simples. E não precisa parar o jogo pra ler, que é o grande inconveninente. No loop da interface você apenas usa peek() e volta pra animação. Se tem algo pra ler você vê o que é, trata e descarta, SEM PARAR O PROGRAMA.

essa parte nao entendi mt bem, sou novato ainda.

 

 

 

adicionado 35 minutos depois

@arfneto @Lucca Rodrigues o meu "jogo" eh do C basico, sem interface nem nada, NÃO eh c++, E eu preciso que meu programa pare no getch(); (ou algum lugar especifico e volte a executar quando o usuario apertar enter) sem importar quantas vezes ele apertou enter antes, por exemplo, eu executo o programa e aperto enter 5x, eu quero que mesmo assim, ele exclua esses 5 enter e na hora que ele aparecer "aperte enter para continuar" ai sim o enter seja valido, e assim sucessivamente, se puderem me mandar um código de como fazer porque sou meio leigo no assunto. 
obrigado.

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

8 horas atrás, felipe pereira forte disse:

pode me explicar melhor como funciona e algum codigo limpando o buffer com ele?

 

 

🤔

#include <windows.h>
int main()
{
    FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE));
};

É só isso mesmo. Como diz o nome: flush console input buffer. Não sobra anda

 

8 horas atrás, felipe pereira forte disse:

o meu "jogo" eh do C basico, sem interface nem nada, NÃO eh c++, E eu preciso que meu programa pare no getch(); (ou algum lugar especifico e volte a executar quando o usuario apertar enter) sem importar quantas vezes ele apertou enter antes, por exemplo, eu executo o programa e aperto enter 5x, eu quero que mesmo assim, ele exclua esses 5 enter e na hora que ele aparecer "aperte enter para continuar" ai sim o enter seja valido, e assim sucessivamente, se puderem me mandar um código de como fazer porque sou meio leigo no assunto

 

Como pode ser um jogo sem interface? Interface é isso: essas coisas que você mostra e essas coisas que você lê.

Se você quer que o programa pare até o cara teclar algo DEPOIS do programa chegar na leitura, descartando tudo que pode já ter digitado ANTES de seu programa chegar na leitura, rode esse comando antes. Nada mais.

 

11 horas atrás, arfneto disse:

Não use essas coisas para streams de entrada. Mesmo que funcionem em algum sistema contraria a definição de flush: o comportamento de fflush() para fluxos de entrada é indefinido. 

 

setbuf() também não é pra isso. Sugiro esquecer.

 

 

 

9 horas atrás, felipe pereira forte disse:

Se está controlando um jogo a partir do teclado em Windows, e precisa usar a console mesmo, recomendo usar apenas PeekConsoleInput(). É mais simples. E não precisa parar o jogo pra ler, que é o grande inconveninente. No loop da interface você apenas usa peek() e volta pra animação. Se tem algo pra ler você vê o que é, trata e descarta, SEM PARAR O PROGRAMA

9 horas atrás, felipe pereira forte disse:

essa parte nao entendi mt bem, sou novato ainda

 

Em um jogo em geral você não pode parar pra ler: você para, ou interrompe, quando lê. Entenda que um read para o jogo. Em geral você não quer isso. O jogo não para. As coisas vão acontecendo. Imagine um jogo com uma mensagem e uma leitura: "Tecle o próximo comando" e ele fica lá parado... E o comando era um tiro...

 

Em geral você quer que a coisa ande e você executa um comando na hora em que o jogador digita o comando.

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

6 horas atrás, arfneto disse:

#include <windows.h> int main() { FlushConsoleInputBuffer(GetStdHandle(STD_INPUT_HANDLE)); };

esse deu certo e fez exatamente como eu precisava, MUITO OBRIGADO me ajudou demais
você sabe me dizer se o getch(); eh a melhor maneira de se esperar o tal ENTER?

 

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

4 minutos atrás, felipe pereira forte disse:

esse deu certo e fez exatamente como eu precisava, MUITO OBRIGADO me ajudou demais
você sabe me dizer se o getch(); eh a melhor maneira de se esperar o tal ENTER

 

Sim, porque faz exatamente o que você precisa nesse caso.

 

Use o simples: fgetc() e leia uma letra por vez. Ou use PeekConsoleInput() e não leia nada. Tenho um exemplo que acho que vou postar aqui depois do ☕ e recomendo muito que rode na sua máquina

 

E tire esse

#include "conio.h" 

de seu programa. Isso é só problema. Isso tem 30 anos e de nada serve há pelo menos 20. 

 

 

adicionado 47 minutos depois

Olá!

 

Então o programa que vou te mostrar
 

Citação

usa peek() para ler o que tem lá pra ler, mas sem ler. E é essencial em jogos

 

Cenário:

 

Vamos começar com o exemplo de que falou: o cara teclou 5 enter e "teste" e seu programa vai chegar lá pra ler. Eis o que o programa mostra

peekA.png.bcbb8f9933f9d073f2fde303142ab0b7.png

 

O programa apenas "espia" --- peek() --- o que tem lá em stdin para ser lido se você entrar com um read() ou getc() ou fgetc() ou scanf() ou fgets() ou sei lá. Mas não lê. Então seu programa continua rodando se você quiser ou precisar, por exemplo rodando o cenário do jogo ou atendendo outro jogador. Só que você pode agir em cima do que foi digitado mas você não leu. E nem ler. E seu programa não para.

 

Acho que entendeu a diferença. Se não entendeu pergunte de novo.

 

O programa só tem dois comandos, '.' e '/' como está escrito na na tela. Se digitar um ponto o programa percebe e limpa o buffer, apaga tudo que não foi lido. Se digitar '/' ele encerra o programa apenas.

 

Ao ler um ponto

 

image.png.767e8b3c6aea041abf5f59b4b07f5880.png

 

Só isso. limpa tudo. 

 

Ao ler "de novo"
 

peekB.png.bf32adad27f2bf4c36ec7b20221eccc7.png

O programa mostra caracteres com espaço :D que não daria pra ver nada ou ENTER pelo código decimal e coloca uma " ' " depois pra indicar.

 

Essa é a maneira de ler por exemplo em um jogo de tiro e de movimento: você trata só por exemplo as teclas de movimento "wsdx" espaço e enter e 'q' para sair do jogo por exemplo. 

Mas não pode executar um read() porque aí para tudo 🤣 certo?

 

E o programa como seria?

#define _CRT_SECURE_NO_WARNINGS
#include "_arfneto.h"

  // ARFNeto '2020

void prepara_tela();

int main(int argc, char** argv)
{
    struct _INPUT_RECORD linha[40];
    HANDLE H = GetStdHandle(STD_INPUT_HANDLE);
    prepara_tela();
    int N = 0;
    int antes = N;
    while (1)
    {
        char c = 0;
        PeekConsoleInput(H, linha, 40, (LPDWORD)&N);
        if (N != antes)
        {   // se mudou algo nessa leitura
            gotoYX(10, 20);
            for (int i = 0; i < N; i += 1)
            {
                if (linha[i].EventType == KEY_EVENT)
                {
                    if (linha[i].Event.KeyEvent.bKeyDown)
                    {
                        c = linha[i].Event.KeyEvent.uChar.AsciiChar;
                        if ((isprint(c)) && (c != ' '))
                            printf("%4c", c);
                        else
                            printf("%4d'", c);
                    };	// if
                    if (c == '.')
                    {
                        Beep(600, 50);
                        FlushConsoleInputBuffer(H);
                        prepara_tela();
                        c = 0;
                    }
                    else
                        if (c == '/')
                        {
                            printf("\n\n\n\n");
                            return 0;
                        }
                };
            };
        };
        gotoYX(11, 8);
        Sleep(1000);
    };  // while()
    return 0;
};

void prepara_tela()
{
    cls();
    gotoYX(12, 8);
    mensagem_em_video_reverso("'.' limpa tudo, '/' termina");
    gotoYX(10, 8);
    mensagem_em_video_reverso("Para ler:");
};
// fim

Pode baixar uma versão compilável aqui é bem pequeno.

 

É claro que tudo gira em torno de chamadas a PeekConsoleInput()

 

image.png.bd2502023ed72e4e45d94d7429c13114.png

 

para ver se entrou algo.

 

Entenda que em Linux que um dia foi Unix, ou MacOS que um dia foi Linux, você pode fazer a mesma coisa. Apenas não me lembro mais de cabeça como eram as chamadas, mas eram em torno de chamadas a ioctl() e não sei sequer se isso ainda existe hoje em dia :) ou se tem melhor maneira. Mas a mecânica é a mesma.

 

Link para o comentário
Compartilhar em outros sites

:D

 

parece que nada mudou desde os '80. Fiquei curioso e vi aqui no WSL


image.thumb.png.6d58ad5a10ed0418386bf0cb8014ea01.png

 

ou seja, é igualzinho. Pode usar ioctl() e as funções em termios.h então deve ter um recurso de peek() aí dentro como era antes.

adicionado 42 minutos depois

continuei lendo :). Nada mudou nisso desde o tempo de ler em C de dentro do programa em COBOL para não parar a comunicação com o mainframe IBM d outro lado do link.

 

Mas acho que a solução simples não é bem com peek(). Apenas usa essas rotinas aí acima e desativa a tal entrada canônica do terminal onde está rodando o programa, o tal console mode do Windows. E aí manipula os parâmetros VMIN e VTIME para que o read() em C retorne e imediato. Ao invés de peek() você lê mesmo, mas o read não para.

 

E essa solução deve ser portável e rodar igual no Windows. Sem mudar nada. Como o PeekConsoleInput() e toda aquela biblioteca de coisas para a console só existe para a console, algo que não tem no Linux, então esse outro caminho pode ser interessante.

Link para o comentário
Compartilhar em outros sites

10 horas atrás, felipe pereira forte disse:

mas não entendi a maneira viável de usa-la

 

Mas eu te mostrei um exemplo e enviei um programa funcionando que mostra

  • como e porque usar FushConsoleInputBuffer()
     
  • o efeito de usar PeekConsoleInput() ANTES de ler pra ver se tem o que você quer lá e eventualmente descartar se for o caso
     
    Citação

    Notou que  programa que te mostrei não tem uma leitura? Rodou o programa ao menos?

     

E o exemplo da situação prática:
 

18 horas atrás, arfneto disse:

maneira de ler por exemplo em um jogo de tiro e de movimento: você trata só por exemplo as teclas de movimento "wsdx" espaço e enter e 'q' para sair do jogo por exemplo. 

Mas não pode executar um read() porque aí para tudo 🤣 certo

 

Que  fazer no fogo de tiro, o exemplo que te dei? Tem os personagens na tela, o cenário... E aí você faz o que? Um prompt? 

printf("EX sobe e desce SD esquerda direita para mover a arma, ESPAÇO para atirar");
printf("Entre com o comando: ");
comando = fgetc(comando, 2, stdin);

Sério? E aí o jogo para até o cara teclar W e ENTER?

 

Rodou mesmo o  programa que te mostrei? 


Entendeu que ele "leu" tudo do teclado mas não tem read? Que você pode controlar o jogo sem ecoar as teclas e sem parar o loop?

 

Mas se estou lendo isso e quero fazer no Unix/Linux/MacOS?


Pois é, eu me antecipei e imaginei antes de tentar que poderia usar termio.h e as funções de que falei, isso no Linux. Mas não dá. Isso nem existe no Windows :( 

Acho que não dá pra escrever uma versão portável usando a mesma técnica. Mas não tem muita dificuldade: use uma das muitas técnicas comuns para saber se está em Windows ou Linux, ou invente uma. Depois um if na leitura e

  • no Windows use PeekConsoleInput() e FlushInputBuffer() é trivial e dá pra ler tudo que tem no buffer
     
  • no Linux use as rotinas que mostrei. O Unix/Linux/Mac não precisa nem de internet e pesquisas e Google: use 
        man termios

    e vai ler tudo que precisa
     

  • Em resumo:
     
    • use tcgetattr() pra ler e salvar a configuração do terminal --- console
    • use tcsetattr() para desligar a entrada canônica --- ICANON
    • e VMIN = o que precisar, tipo 1 para ler uma única tecla
    • e VTIME=0 pra retornar de imediato
    • pronto: use read(). Fiz isso por anos e anos em COBOL/C. Nos primeiros nem tinha essa preocupação de portar pra Windows porque não tinha Windows. A preocupação era portar de e para o mainframe que rodava  OS/MVS ou DOS/VMS ou VM/CMS, e hoje em dia roda Linux :) 
       
  • E use um if() para escolher entre o modo windows e o modo Linux
     
  • Me pague uma 🍕
     
  • Claro, use um termio.h fake no Windows e um windows.h fake no Linux para o compilador não se queixar

 

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...