SamL
(usa XUbuntu)
Enviado em 21/04/2021 - 17:51h
SrKon escreveu:
SamL escreveu:
Sugiro que você estude pelo menos análise léxica e máquinas de estados, pois, é justamente com algo assim que você poderá escrever um código mais limpo e usando menos comandos.
Por exemplo:
Com um analisador léxico, você pode transformar cada palavra como um token do tipo identificador, e ir contando:
/**
* @file minilexer.c
* @author Samuel Leonardo
* @brief Mini analisador léxico de uma string
* @version 0.1
* @date 2021-04-20
*
* @copyright Copyleft (c) 2021
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main ( ) {
//modifique esta string para ver se o analisador funciona
//observe que, só coloquei o caractere ' ' (espaço) como separador das palavras
//você pode modificar facilmente para outros caracteres como o \n \t etc e tal
//NOTA: também não coloquei para ignorar pontuaçlão, por isso,
//algo como "aqui," é encarado como uma palavra
const char * str = "String teste Com Char MAIUSCULOS!";
//estado do analisador léxico
int estado = 0;
//posição do caractere atual
int p = 0;
int tamanho = strlen(str);
int qtdePalavras = 0;
while (str[p]) {
//estado de lendo um caractere
if (estado == 0) {
if (str[p] != ' ') {
//vai para estado de lendo identificador (palavra sem espaço)
estado = 1;
}
else {
// move a posição do caractere atual
p++;
}
}
// lendo um identificador
else if (estado == 1) {
int inicio = p;
int fim = p + 1;
while (str[fim] && str[fim] != ' ') {
fim++;
}
//verifica agora se o fim NÃO é o char '\0' ou um ' ' (espaço)
if (str[fim] == '\0' || str[fim] == ' ') {
// se estamos aqui, é de se supor que estamos lendo um caractere do alfabeto
// seria mais interessante ter verificado isso no if daqui
// agora, incrementa o contador de palavras
qtdePalavras++;
}
// depois, posiciona o contador de posição na posição do fim
p = fim;
// volta ao estado de lendo caractere
estado = 0;
}
}
printf("Frase: %s \n\tem %d palavras\n", str, qtdePalavras);
return 0;
}
Os conceitos utilizados nesse programa acima são:
1-análise léxica
2-máquina de estados finitos
Daria até para desenhar num papel como funciona a transição de estados, fica até mais fácil de ler assim. Mas prefiro não fazer isso por questão de tempo.
____________________________________________
https://nerdki.blogspot.com/ acessa aí vai lá, é grátis!
Capeta (demo) do meu trabalho:
https://github.com/cpusam
Eu agradeço a sua ajuda. Gostaria que explicasse o
str[fim] && str[fim]
,
não entendi porque repetir esse comando, para mim é a mesma coisa.
Vou supor que você esteja falando da linha do
while (str[fim] && str[fim] != ' ')
Esse comqando ele quer dizer o seguinte: ENQUANTO str[fim] for diferente de zero E str[fim] for diferente do caractere espaço execute o while
Então, é seguinte, se por exemplo, o valor numérico do caractere str[fim] for igual ao '\0', que é o caractere de fim de string, que tem valor igual a zero, dai o while acabaria porque já leu toda a string e a variável fim seria igual ao tamanho da string. Mas observe que, se por acaso NÃO ler um caractere igual a zero (ou '\0'), dai pode ser que vamos ler um espaço na string, e se por acaso for um espaço? Daí, o
str[fim] != ' ' vai ser falso, pois na posição fim na str tem um espaço.
Por isso, quando sair do while, nós vamos poder ter duas situações distintas:
1-ou na posição str[fim] tem um caractere nulo (caractere '\0')
2-ou na posição str[fim] tem um caractere espaço
A situação 1 acima só acontece com a ÚLTIMA palavra, enquanto que a situação 2 acontece quando é da primeira até a penúltima palavra na str.
Por isso, no if mais abaixo, eu coloquei um OU (operador ||) pra justamente verificar essas duas situações e depois incrementar o contador de palavras.
Não é redundante/ desnecessário colocar
estado = 0;
Faltou você dizer em quais linhas você tá falando, mas vou explicar uma a uma.
Numa máquina de estados finitos, sempre é preciso dizer qual o estado inicial da máquina, o estado que ela deve começar trabalhar.
Então, na linha int estado = 0; (antes do
while(str[p]) , eu estou indicando: começe a máquina com estado inicial sendo o 0.
Agora observe o seguinte, dentro do while, exatamente nos if's, existe um estado = 0 dentro do if (estado == 1), sabe por quê? Porque como esses dois if's, o if (estado == 0) e o else if (estado == 1), estão dentro do while (str[p]). Ou seja, quando eu faço estado = 1 dentro do if (estado == 0), eu estou mudando o fluxo do processamento do while pra dentro do segundo if (o else if (estado == 1), compreende? E dentro do
else if (estado == 1) , eu novamente mudo o fluxo de processamento pra dentro do primeiro if, ou seja, coloco estado = 0 pra ai sim na próxima vez que o while(str[p]) voltar executar, ele entra dentro do primeiro if (estado == 0).
Eu preciso fazer com que o código repita isso até que o arquivo acabe. Usei uma função, mas não funcionou. Usei, também, outro laço colocando isso dentro dele, nada. Então algumas dica de como repetir para várias linhas?
É bem simples, vou te explicar o processo, eu não quis fazer o programa lendo do arquivo pra que você pudesse pensar sozinho, mas vou explicar um passo a apasso mais mastigado.
Então, você tem que saber visualizar um arquivo de texto mentalmente. Por exemplo, imagina o seguinte, você abriu um arquivo de texto num editor, dai o que o editor faz? Coloca a posição do cursor no primeiro caractere da primeira linha (aquele palitinho branco que aparece piscando).
Então, é só imaginar que, a str do meu programa seja o arquivo de texto com todo conteúdo e o str[p] seja o caractere atual.
Sendo assim, o str[0] é o primeiro caractere da primeira linha (o caractere mais a esquerda da linha 1)
Se tu olhar bem no meu programa, ele trata algumas situações comuns, por exemplo: fim da string é o '\0' e o fim do arquivo seria o EOF, ou seja, é o mesmo que dizer:
while (str[p])
equivale a isso abaixo
while (!feof(arquivoTxt))
Mas e você pode pergunatr: e como faço pra ler do arquivo?
Eu digo: simples, use fgetc:
exemplo:
//abra um arquivo de texto aqui, chame de arquivoTxt
//no loop de caracteres
while (!feof(arquivoTxt)) {
int caractere = fgetc(arquivoTxt);
}
fgetc cada vez que é executada ele lê o caractere do arquivo e retorna ele. Só que o seguinte, quando ela faz isso, o cursor do arquivoTxt vai ser movido pro próximo caractere. Por isso, se tu executar uma vez, vai ler o primeiro caractere e mover o curso do arquivoTxt pra direita num txt. Depois, executa de novo, retrona o caractere e move o cursor novamente, depois faz de novo isso até o fim do arquivo.
Quando chega no fim do arquvio, o while(!feof(arquivoTxt)) vai ser falso e quebrar o loop.
Então, pra resumir:
-ao invés de usar str[p] ou str[fim] no meu programa, você deve substituir pelo caractere lido com fgetc do arquivoTxt. Mas observe que não será fácil fazer isso, porque dentro do if (estado == 1) existe outro while que lê caracteres da str, ou seja, tem que colocar outro fgetc pra ler o próximo caractere de dentro do arquivoTxt.
____________________________________________
https://nerdki.blogspot.com/ acessa aí vai lá, é grátis!
Capeta (demo) do meu trabalho:
https://github.com/cpusam