paulo1205
(usa Ubuntu)
Enviado em 11/12/2019 - 18:34h
Luispaulo01 escreveu:
Meu programa tem que receber um número por extenso entre 0 e 999 e converter para inteiro. Mas até agora, só converte apenas até o 9.
A questão que você tem de resolver é de construir um analisador léxico e gramatical. Você já leu a respeito desse tema especificamente?
Uma forma comum de tais analisadores funcionarem é dividir a
string de entrada em símbolos ou
tokens, e usar uma máquina de estados que restringe quais símbolos são válidos em cada estado e quais os próximos estados em função de cada possível símbolo recebido. A máquina opera, então, com um estado atual, que consome o próximo símbolo da lista de símbolos e, em função do símbolo recebido, define qual será o estado seguinte, até que acabem os símbolos ou se atinja uma situação de erro (por exemplo: fim da lista de símbolos quando se deveria ter um símbolo a mais, símbolos fora de ordem, ou símbolos adicionais depois de um símbolo que deveria ser um símbolo terminal, entre outras situações que você possa identificar).
Recomendo, portanto, que você leia a respeito do assunto que você tem de tratar, e reescreva seu programa de uma forma correspondente.
Além disso, permita-me apontar alguns erros mais básicos, que não têm a ver com a natureza do problema a ser tratado, mas dizem respeito à própria linguagem C.
#include <stdio.h>
#include <string.h>
int main(){
int num,i;
char unid[10][5]= {"zero","um","dois","tres","quatro","cinco","seis","sete","oito","nove"};
Aqui você declara um
array com dez elementos em que cada elemento é um
array com cinco elementos, cada um deles contendo um valor variável do tipo
char. Note que dois dos elementos do
array indicado por
unid esbarram no limite de cinco caracteres que cada um deles pode ter. No caso de
"quatro", você teria de ter sete caracteres (seis de texto, mais um do
byte nulo que funciona como marcador do fim da
string) para que tal elemento fosse uma
string válida. Essa extrapolação de tamanho deveria ser identificada pelo compilador como erro, e deveria interromper a compilação imediatamente. Se não foi apontada como erro, seu compilador tem uma falha de implementação, e você deveria reportar esse problema ao fabricante, e trocar de compilador até o erro ter sido resolvido.
O outro elemento problemático é
"cinco", porque ele teria de ter seis caracteres de largura (os cinco do texto, mais um para o terminador). Mas como você tem apenas cinco caracteres de largura e o texto entre aspas também tem cinco caracteres, o C assume que você propositalmente quis suprimir o
byte terminador, mas essa supressão implica que o conteúdo de
unid[5] não será uma
string válida, justamente por causa da falta do terminador. (Se você utilizar um compilador C++, essa supressão do terminador não é feita, e ela é tratada como erro, assim como o caso anterior.)
char dez[10][20]={"Zero","Dez","Vinte","Trinta","Quarenta","Cinquenta","Sessenta","Setenta","Oitenta","Noventa"};
char cent[10][20]={"Zero","Cento e","Duzentos e","Trezentos e","Quatrocentos e","Quinhentos e","Seiscentos e","Setecentos e","Oitocentos e","Novecentos e"};
char excecao[10][20]={"Dez","Onze","Doze","Treze","Catorze","Quinze","Dezesseis","Dezessete","Dezoito","Dezenove"};
Para todos esses
arrays de
arrays com elementos variáveis, eu sugiro trocar por
arrays de ponteiros constantes para elementos constantes, e preferencialmente trocando a alocação automática por alocação estática. Por exemplo, a definição abaixo pode ser usada para símbolos que seguramente provocam um estado seguinte em que o fim da
string de entrada tem de ter sido atingido.
static const char *const terminais[]={
"zero", "um", "dois", "tres", "quatro", "cinco", "seis", "sete", "oito", "nove",
"dez", "onze", "doze", "treze", "catorze", "quatorze", "quinze", "dezesseis", "dezessete", "dezoito", "dezenove",
"cem",
NULL // Este ponteiro nulo funciona como marcador do fim da lista, caso você queira percorrer o arrau usando um ponteiro.
};
static const size_t n_terminais=(sizeof terminais/sizeof terminais[0])-1; // Um contador do nº de elementos válidos do array ‘terminais’, sem que
// você mesmo tenha de contá-los (o ‘-1’ é para desconsiderar o elemento NULL).
// Este contador é útil para percorrer o array usando índices.
char num_ext[999];
printf("Digite um numero: ");
scanf("%[^\n]s", num_ext);
Erro comum, mas ainda assim um erro: a
string de formatação nunca será plenamente satisfeita. O erro é pensar que o que vai entre colchetes é um modificador infixado da conversão “
%s”, o que não é verdade. O que acontece realmente é que “
%[” é um outro tipo de conversão, com regras distintas de “
%s”, e que recebe um modificador/argumento posfixado, que é terminado pelo caráter “
]”. O “
s” que vem após o fechamento do colchete não é parte de conversão nenhuma, mas um texto arbitrário que
scanf() vai tentar encontrar após a conversão,
e que necessariamente vai falhar, pois, se a conversão tiver sido bem sucedida (i.e.: se tiver lido um ou mais caracteres diferentes de quebra de linha), o que virá após a conversão será necessariamente uma quebra de linha.
Além desse erro, existem outros problemas que podem e devem ser melhorados. Por exemplo, você deveria checar o valor de retorno de
scanf() para ter certeza de que a conversão foi bem sucedida. Além disso, como você tem um tamanho limitado para o
array que vai armazenar o texto digitado, deveria instruir
scanf() a limitar a quantidade de caracteres que ela pode tentar colocar nesse
array, a fim de evitar possíveis erros de
buffer overflow, cujos resultados são imprevisíveis. Como sugestão, você poderia usar o seguinte.
int a, b;
a=b=0;
if(scanf("%998[^\n]%n%*1[\n]%n", num_ext, &a, &b)<1 || b<=a){
// Leitura inválida (scanf(...)<1) ou truncada (b<=a).
// Cabe a você decidir o que fazer em tais casos, mas não deve prosseguir como se
// a leitura tivesse sido feita com sucesso.
}
else{
// Leitura bem sucedida.
// Note que, se a leitura tiver sido bem sucedida e sem truncamento, o terminador da linha
// já terá sido consumido, de modo a não deixar vestígios no buffer de entrada (que é outro
// problema comum de acontecer).
}
Em lugar de percorrer a
string de entrada caráter a caráter, seria interessante separá-la em
tokens. Para tanto, seria interessante você estudar sobre
strsep(),
strtok(),
strspn() e
strcspn(), entre outras.
if(strcmp(num_ext,unid[i])==0){
printf(" => %s eh igual a -> %d\n",num_ext,i);
}
else if(strcmp(num_ext,dez[i])==0){
printf(" => %s eh igual a -> %d\n",num_ext,i);
}
else if(strcmp(num_ext,cent[i])==0){
printf(" => %s eh igual a -> %d\n",num_ext,i);
}
else if(strcmp(num_ext,excecao[i])==0){
printf(" => %s eh igual a -> %d\n",num_ext,i);
}
Em todas as comparações acima, note que existe um problema:
strcmp() considera letras maiúsculas como distintas das letras minúsculas, de modo que se alguém digitar “
Cinco” mas o símbolo definido no programa for “
cinco”,
strcmp() nunca dará resultado de que houve correspondência. Provavelmente seria interessante você converter toda a
string de entrada para minúsculas antes de começar a varrê-la, e usar todos as definições de símbolos válidos em letras minúsculas (ou tudo maiúsculo, se você preferir), ou então você deve usar outra função de comparação, que não faça distinção entre maiúsculas e minúsculas. Infelizmente, até onde eu sei, não existe tal função na biblioteca padrão do C (o mundo UNIX tem
strcasecmp(), o mundo Windows tem
stricmp(), mas nenhuma das duas variações é universalmente aceita). Pode ser que
strcoll() funcione como comparação que não distingue maiúsculas de minúsculas, mas isso não é garantido, e você teria de se preocupar com o uso de internacionalização e localização no seu programa, o que eu acho excessivo num exercício como esse.
... “Principium sapientiae timor Domini, et scientia sanctorum prudentia.” (Proverbia 9:10)