Índice
Este blog deduzirá principalmente um miniaplicativo de barra de progresso em execução no Linux passo a passo e usará o editor vim e o compilador gcc . Se você não estiver familiarizado com esses dois softwares, clique no link e aprenda com este blog. A barra de progresso applet é exibido da seguinte forma:
a.\r && \n
Existem muitos caracteres na linguagem C, mas eles podem ser divididos macroscopicamente em dois tipos: caracteres visíveis e caracteres de controle
Caracteres exibíveis: A, a, B, c... e outros caracteres
Caracteres de controle: \n (retorno de carro), \t (tabulação horizontal), \r (alimentação de linha), etc.
\r
Aqui precisamos usar e \n
dois caracteres no sistema Linux
Geralmente, quando usamos a linguagem C \n
para alterar a linha ou usamos a tecla Enter do computador para alterar a linha, mudamos diretamente para a posição mais à esquerda da próxima linha
int main()
{
printf("Hello\nWorld!");
return 0;
}
Mas, na verdade, essas são duas ações, apenas na categoria de linguagem. A linguagem C usa \n
as duas ações de mudar para a próxima linha e retornar para a atual mais à esquerda .
Como nosso teclado antiquado anterior, a tecla Enter é diferente da tecla Enter atual, indicando que a tecla é a soma de duas ações.
Portanto, a nova linha a que geralmente nos referimos é composta por essas duas ações.
Depois de entender o conhecimento acima, vamos dar uma olhada no \r
significado \n
e
\r: retorno de carro (retorna ao extremo esquerdo da linha atual)
\n: Nova linha (mude para a próxima linha)
Quando usamos a linguagem C \n
para quebra de linha, no nível da linguagem, duas ações de quebra de linha + retorno de carro são executadas por padrão.
2. O conceito de buffer de linha
pergunta:
Vamos primeiro observar a diferença entre os dois trechos de código a seguir após a execução no Linux
-
O arquivo executável das duas partes do código será nomeado MyTest e executado para exibir
-
As funções serão usadas no código
sleep
, você pode usar o comando man para encontrá-lo no manual nº 3 ( comandos comuns do Linux )man 3 sleep
- Função: Pausa no código atual, em segundos.
código 1
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\n");//使用换行符'\n'
sleep(3);//暂停3秒后继续运行
return 0;
}
código 2
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\r");//使用回车符'\r'
sleep(3);//暂停3秒后继续运行
return 0;
}
De acordo com nossa lógica normal, os dois trechos de código devem imprimir algo, mas o código 2 apenas executa o modo de suspensão e nada é impresso, por quê? Tem algo
\r
a\n
ver?
Este problema envolve o conceito da área de cache (vamos entender brevemente aqui, o restante do conteúdo do buffer ficará em um blog posterior)
responder:
Vamos dar uma olhada no código a seguir e seus resultados em execução:
código 3
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world");
sleep(3);//暂停3秒后继续运行
return 0;
}
Podemos ver os resultados acima, parece que desta vez a função sleep é executada primeiro, após a execução do printf e após a string de saída, a linha de comando é gerada.
-
O código em linguagem C executa uma estrutura sequencial , que deve ser executada em sequência e lógica de código, portanto printf deve ser executado primeiro e depois sleep.
-
Printf é executado e precisa haver um local para armazenar strings temporariamente, e esse local é a área de buffer .
-
Quando o código é executado, a área do buffer é atualizada e a string é impressa na tela. Neste momento , a posição do cursor (cursor verde) está atrás da string.
-
No Linux, toda vez que os dados são impressos no monitor, eles partem da posição do cursor, ou seja, o cursor coincide com o monitor, onde está o cursor, a impressão começa ali.
-
Portanto, a linha de comando será impressa diretamente após a string. ( O código 1 tem uma quebra de linha e o cursor está em uma nova linha)
-
Buffers de plataformas diferentes têm expressões diferentes. No Linux, o buffer tem sua própria estratégia de atualização (muitas)
O que estamos vendo agora é o cache de linha , que atualizará a área de cache em seis situações (apenas três são introduzidas e o restante é irrelevante para este artigo)
- Um caractere de nova linha é encontrado, como:
\n
. ( O código 1 primeiro vê a string e depois dorme) - quando o programa termina. ( no caso do código três )
- atualização ativa
- Um caractere de nova linha é encontrado, como:
A partir do conteúdo acima, podemos analisar os motivos da execução malsucedida do código 2 :
Depois que a função printf é executada, o buffer não é atualizado e os dados são salvos no buffer. Depois que o sleep é executado, a string é impressa na tela, mas no conteúdo anterior nós
\r
a especificamos como um retorno de carro e o cursor voltou para o final da linha à esquerda, então a linha de comando é impressa no início do cursor, sobrescrevendo o conteúdo da string. Não podemos ver mais nada.
Então, há uma maneira de consertar isso? Resposta: Não consigo pensar nisso, desde que o \r
coloquemos no final da string, a string será substituída pela linha de comando. Mesmo se usarmos outros caracteres de controle para exibir o resultado, o resultado é diferente do que queremos afinal.
Aqui podemos usar o terceiro método de atualização do cache descrito acima para verificar se nossa resposta está correta e ver a string aparecer e desaparecer na tela.
Detecção:
Se quisermos atualizar ativamente o buffer, precisamos usar fflush
funções.Também podemos usar o comando man para encontrá-lo no manual nº 3. Comandos comuns do Linux
man 3 fflush
O código de teste é o seguinte:
#include<stdio.h>
#include<unistd.h>
int main()
{
printf("hello world\r");//使用回车符'\r'
fflush(stdout);//自动刷新缓存区
sleep(3);//暂停3秒后继续运行
return 0;
}
Podemos ver pelos resultados que este é o caso, seu cursor é movido para a extrema esquerda e é substituído quando a linha de comando é impressa, e não podemos ver o resultado no Code 2 .
expandir
Tudo impresso no visor é um caractere
printf("%d",123);
//打印的123,分别为'1'、'2'、'3'
3. Barra de progresso
Exibição de resultados:
Podemos usar o conhecimento do buffer acima para implementar um programa tão pequeno no Linux usando a linguagem C
Podemos simplesmente dividir um programa tão pequeno em quatro partes, conforme mostrado na figura abaixo:
1. Barra dinâmica de progresso
O gráfico do crescimento da barra de progresso eu defini como =
, e começo com o >
símbolo
A configuração da barra de progresso é de 0% a 100% (0% não tem progresso), precisamos de 100 pedaços de tamanho =
e mais um >
, e adicionar um terminador no final \0
, um total de 102 bytes são armazenados, o que é o caractere para armazenar a barra de progresso O tamanho da matriz do tipo é 102.
Nota: Depois de criar o array, você precisa continuar a inicializá-lo para torná-lo 102 bytes, para que ele \0
pare com precisão toda vez que atingir a posição atualizada.
Queremos que seja atualizado a cada 1% da carga. Precisamos colocar o array impresso no loop e \r
colocá-lo atrás da string a ser impressa. Use fflush(stdout)
-o para atualizar automaticamente a área do buffer. Use usleep
(sleep unit segundos, usleep unit Sutil, você mesmo pode tentar ver qual é o mais adequado) Pause cada ciclo e continue, para que você possa mostrar a forma da barra de progresso subindo.
2. Porcentagem de progresso
Imprimimos a barra de progresso no loop, desde que o número de loops seja impresso ao imprimir. Observação: o sinal de porcentagem significa que %%
não pode ser usado \%
e será emitido um alarme. Se a compilação na primeira plataforma falhar, será será mostrado.
Modifique o código acima da seguinte maneira
printf("[%-100s][%d%%]\r",bar,i);
3. Pequenas decorações
No final da barra de progresso, adicionamos um cursor giratório para torná-la mais vívida,
Aqui | / - \
quatro símbolos são usados para representar o cursor e armazenados em uma matriz. Observação: \ representa um caractere de deslocamento e dois \ são usados para representar um
Girar no sentido horário: "|/-\"
Girar no sentido anti-horário: "|\-/"
Sentido horário é usado aqui, e o código modificado completo é o seguinte:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define N 101
#define STYLE '='
int main()
{
char bar[N];
memset(bar,'\0',sizeof(bar));
char lable[4] = "|/-\\";
int i=0;
while(i<=100)
{
printf("[%-100s][%d%%][%c]\r",bar,i,lable[i%4]);
fflush(stdout);
usleep(100000);
bar[i++] = STYLE;
if(i!=100) bar[i] = '>';
}
printf("\n");
return 0;
}
4. Cor
Existem várias maneiras de alterar a cor da barra de progresso que exibimos. Aqui, mostro apenas uma. Os interessados podem pesquisar por conta própria.
//格式
printf("\e[31m字符串\e[0m");
//31:前景色
//\e[31 :开头
//\e[0m :终止,使改变的颜色只在字符串内
cor de primeiro plano (cor da fonte)
personagem | cor |
---|---|
30 | Preto |
31 | vermelho |
32 | verde |
33 | amarelo |
34 | azul |
35 | Roxa |
36 | verde escuro |
37 | Branco |
Com esse conhecimento, podemos mudar a cor da barra de progresso, tornando-a vermelha, o código é o seguinte:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#define N 101
#define STYLE '='
int main()
{
char bar[N];
memset(bar,'\0',sizeof(bar));
const char* lable = "|/-\\";
int i=0;
while(i<=100)
{
printf("[\e[31m%-100s\e[0m][%d%%][%c]\r",bar,i,lable[i%4]);
fflush(stdout);
usleep(100000);
bar[i++] = STYLE;
if(i!=100) bar[i] = '>';
}
printf("\n");
return 0;
}
A cor e o formato da barra de progresso não são os únicos. Se você estiver interessado, pode pesquisar mais conteúdo e criar você mesmo uma barra de progresso especial.