Problema no código [RESOLVIDO]

1. Problema no código [RESOLVIDO]

Ricardo
r_mil

(usa Ubuntu)

Enviado em 27/06/2015 - 19:00h

Pessoal, estou com um problemão com o código abaixo, gostaria muito da opinião de vocês para resolvê-lo.
O Programa abre o arquivo contido em uma lista de arquivos (“lista_homologos.txt”) e o trata.
Nessa lista, existe apenas um arquivo (“9998_ENSCJAT00000011695.faa”), cujo o conteúdo é este:

>ENSCJAT00000011695 [Callithrix jacchus] | aligned:1-461 (461)
MATVATCGGDAVGVGSAVATASKSNVTSFQRRGPRVSGTNDSGPRLVSITGTRPSVRNGQLL


O programa trata apenas a primeira linha, guardando o código “ENSCJAT00000011695” na matriz de caracteres e imprimindo depois a matriz na tela e no arquivo de saída, (“results.txt”).

O problema que está ocorrendo é que no arquivo “results.txt” estão sendo impressos caracteres-lixo ao invés da string desejada. Tentei postar os caracteres aqui mais a página não comporta.

O mais impressionante é que quando abro no terminal do arquivo de saída, usando o comando “more results.txt”, ele mostra a string desejada corretamente.

Não sei onde está o erro. Estou usando Gedit para programar e abrir os arquivos de texto.
Não encontrei nenhum erro no meu programa nem no Gedit.

Preciso muito de uma direção, realmente não sei o que está acontecendo.

Segue o código:
// Programa responsavel por produzir a matriz de homologos a partir dos dados brutos oriundos do get_homologues.

# include <stdio.h>
# include <math.h>
# include <string.h>



void LimpaMatriz (char M[100][187]);
void LimpaVetor (char buffer1[]);
void PulaCaracter (char A[]);
void Especie (char M[100][187], char A[], char B[]);
void ImprimeMatriz (FILE * fres, char M[100][187]);




int main (){

FILE * flis; // ponteiro para a lista de grupos homologos
FILE * farq; // ponteiro para o arquivo de grupos homologos
FILE * fres; // ponteiro para o arquivo de resultados

int i = 0; // contadores gerais para o programa
int j = 0;
int l = 0;
int n = 0;


char A [50]; // buffer para captura dos codigos dos transcritos
char B [50]; // buffer para captura dos nomes das especies

char buffer1 [50]; // buffer para a captura do nome de cada arquivo listados em lista_homologos.txt
char buffer2 [100000];

memset (A, 0, 50); // limpeza dos vetores...
memset (B, 0, 50);
memset (buffer1, 0, 50);
memset (buffer2, 0, 100000);


char M[100][187];
LimpaMatriz (M); // limpeza e preparacao da matriz


flis = fopen ("lista_homologos.txt", "r+"); if (flis == NULL) {printf ("\nErro ao se abrir flis\n");}
while (fgets (buffer1, 50, flis) != NULL){
LimpaVetor(buffer1); // limpando o caracter de '\n' existente nos nomes dos arquivos

farq = fopen (buffer1, "r"); if (farq == NULL) {printf ("\nErro ao se abrir farq\n");}
while (fgets (buffer2, 100000, farq) != NULL){

if (buffer2[0] == '>'){
sscanf (buffer2, "%s %s", A, B);
PulaCaracter (A);
PulaCaracter (B);
Especie (M, A, B);
}
}
fres = fopen ("results.txt", "w"); if (fres == NULL){ printf ("\nErro ao se abrir fres!\n");}
ImprimeMatriz (fres, M);
fclose (farq);
}
fclose (fres);
fclose (flis);
}


// funcao que limpa e prepara a matriz para o trabalho
void LimpaMatriz (char M[100][187]){
int i;
int j;
for (i = 0; i < 100; i++){
for (j = 0; j < 187; j++){
M[i][j] = 32; //32 e o valor ascii para nulo
}
}
}

// retira o caracter '\n' do vetor, possibilitando abrir os arquivos da lista
void LimpaVetor (char buffer1[]){
int i;
for (i = 0; i < strlen(buffer1); i++){
if (buffer1[i] == '\n'){
buffer1[i] = '\0';
}
}
}

// funcao que retira os primeiros caracteres dos vetores, para melhorar a estetica dos nomes
void PulaCaracter (char A[]){
int i;
for (i = 0; i < strlen(A); i++){
A[i] = A[i+1];
}
}

// funcao que reconhece de qual especie a sequencia faz parte
void Especie (char M[100][187], char A[], char B[]){

int nA = 0; // contadores para o preenchimento das linhas da matriz
int nB = 0;
int nC = 0;
int nD = 0;
int nE = 0;
int nF = 0;
int nG = 0;
int nH = 0;
int nI = 0;
int nJ = 0;

if (strcmp ("Homo", B) == 0){ strcpy (M[nA], A); nA++;}
if (strcmp ("Pan", B) == 0){ strcpy (M[nB], A); nB++;}
if (strcmp ("Gorilla", B) == 0){ strcpy (M[nC], A); nC++;}
if (strcmp ("Pongo", B) == 0){ strcpy (M[nD], A); nD++;}
if (strcmp ("Nomascus", B) == 0){ strcpy (M[nE], A); nE++;}
if (strcmp ("Macaca", B) == 0){ strcpy (M[nF], A); nF++;}
if (strcmp ("Callithrix", B) == 0){ strcpy (M[nG], A); nG++;}
if (strcmp ("Tarsius", B) == 0){ strcpy (M[nH], A); nH++;}
if (strcmp ("Otolemur", B) == 0){ strcpy (M[nI], A); nI++;}
if (strcmp ("Microcebus", B) == 0){ strcpy (M[nJ], A); nJ++;}
//memset (B, 0, 50);
}

// funcao impressora - imprime a matriz de caracteres
void ImprimeMatriz (FILE * fres, char M[100][187]){
int i;
int j;
for (i = 0; i < 100; i++){
for (j = 0; j < 187; j++){
printf ("%c", M[i][j]);
fprintf (fres, "%c", M[i][j]);
}
printf ("%c",'\n');
fprintf (fres, "%c", '\n');
}
}





  


2. Re: Problema no código

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/06/2015 - 02:25h

Alguns comentários:

1) Procure dar mais sentido ao código, para que quem o lê tenha mais facilidade de ver se tudo está como deveria, e também para facilitar a manutenção do programa ao longo de sua vida útil.

Digo isso por causa do monte de constantes numéricas que aparecem no programa, algumas delas relacionadas entre si, mas sem uma relação evidente. Uma prática mais recomendável seria usar símbolos com nomes descritivos ou construções da linguagem que evidenciem o sentido do que você quer fazer.

Para ter uma ideia de sobre o que estou falando, compare os seguintes códigos.

char buffer[50];

/* várias linhas abaixo */
memset(buffer, 0, 50);

/* várias outras linhas abaixo */
for(n=0; n<50; n++){
/* ... */
if(buffer[n]=='\n'){
/* faz alguma coisa */
}
/* ... */
}


#define BUFFER_SIZE 50

char buffer[BUFFER_SIZE];

/* várias linhas abaixo */
memset(buffer, 0, BUFFER_SIZE);

/* várias outras linhas abaixo */
for(n=0; n<BUFFER_SIZE; n++){
/* ... */
if(buffer[n]=='\n'){
/* faz alguma coisa */
}
/* ... */
}


Se algum dia você quiser mudar o tamanho do buffer, com a primeira versão você teria que localizar todos os lugares do código em que o tamanho do buffer apareça, e fazer alterações em todos esses lugares; esquecer um lugar implica embutir um bug no programa, geralmente difícil de ser localizado. Com a segunda, bastaria mexer em uma só linha.

Note que as chamadas a memset() nos exemplos acima (e também no seu programa original) usa implicitamente o tamanho de cada elemento do array ser igual a 1 -- o que é verdade para o tipo char, que tem tamanho igual a 1 por definição do padrão da linguagem. Se fosse um array de outro tipo, como int, long ou double, você teria de fazer uma multiplicação do número de elementos pelo tamanho de cada elemento ao chamar memset().

#define N_ITENS 50
double preco_itens[N_ITENS];

/* Zera o preço de todos os itens. */
memset(preco_itens, 0, N_ITENS*sizeof preco_itens[0]);


Se for um array nativo (mas não um array dinâmico implementado via ponteiro nem argumento de função), o compilador consegue calcular o tamanho do array inteiro. O seguinte código também funciona, e talvez seja até mais fácil de ler e entender.

memset(preco_itens, 0, sizeof preco_itens); 


Você também consegue saber o número de elementos de um array (de novo, só de array nativos, não de arrays implementados via ponteiros nem parâmetros de função) por meio de uma divisão do tamanho total pelo tamanho de cada elemento.

for(n=0; n<sizeof preco_itens/sizeof preco_itens[0]; n++){
/* Manipula elemento ‘n’ de preco_itens */
}


2) A função LimpaVetor() tem problemas.

O primeiro problema é o próprio nome da função, que não reflete exatamente o que ela faz. Uma vez que a operação que ela realiza é remover a marca de fim de linha do final de uma string, um nome como RemoveLF() seria mais adequado.

Outro problema é a forma como você implementou. Você faz um loop usando como condição de parada uma comparação com o valor devolvido por strlen(), só que strlen() também é uma função que varre a string. Desse jeito, você transformou uma operação de complexidade linear (proporcional ao tamanho da string) em complexidade quadrática (proporcional ao tamanho da string elevado ao quadrado). A forma mais simples e correta de fazer seria a seguinte.

int last=strlen(string)-1;
if(last>=0 && string[last]=='\n')
string[last]='\0';


3) Você repete o erro de transformar o linear em quadrático também em PulaCaracter().

4) A função PulaCaracter() seria desnecessária se você já fizesse a leitura de uma forma mais esperta. Como você já está usando sscanf(), você pode embutir na string de formatação o descarte dos respectivos primeiros caracteres que, de outra forma, iriam para os arrays A e B. Veja como:

while (fgets (buffer2, sizeof buffer2, farq) != NULL)
if(sscanf(buffer2, ">"" ""%*c""%49s"" ""%*c""%49s", A, B)==2){
A[49]=B[49]='\0';
Especie (M, A, B);
}


Eu já dividi a string de formatação em blocos (uma sintaxe que é válida, mas você provavelmente vai preferir escrever apenas «"> %*c%49s %*c%49s"») para ajudar a explicar.

  - «">"» procura exatamente o caráter '>' na primeira coluna da linha. Se não achar, aborta a função, retornando o valor zero (mesmo efeito do if que você tinha usado).
  - «" "» faz com que se ignore uma quantidade qualquer (inclusive zero) de espaços em branco (esse comportamento fica implícito no "%s" da chamada que você fez a sscanf(), então eu o explicito aqui).
  - «"%*c"» é como "%c", que lê um caráter, mas o flag '*' faz com que esse dado lido seja descartado, em vez de ser guardado numa variável. Esse descarte permite dispensar a chamada a PulaCaracter(). Lembre-se que "%c" (e "%*c") não faz o descarte implícito de espaços em branco, e é por isso que eu tive de explicitar o descarte, acima.
  - «"%49s"» lê a sequência de zero ou mais (com no máximo 49) caracteres diferentes de espaço, colocando-os no array passado como argumento correspondente. O tamanho é limitado a no máximo 49 caracteres para não exceder o tamanho declarado dos arrays, e ainda deixar um espaço para o byte nulo em cada um deles.

Eu sei que essas constantes inteiras na string de formatação e depois na hora de garantir a presença de um byte nulo contrariam o que eu disse na primeira observação. Aqui foi só para dar uma ideia. Um exemplo mais completo envolveria criar no programa mais um array, que receberia dinamicamente uma string de formatação que acomodasse os tamanhos de A e de B.

char farq_format_str[50];

if(
snprintf(
farq_format_str, sizeof farq_format_str,
"> %%*c%%%zds %%*c%%%zds",
sizeof A - 1, sizeof B - 1
) >= sizeof farq_format_str
){
fprintf(stderr, "Erro ao formar string de formatação.\n");
exit(1);
}

/* ... */

while (fgets (buffer2, sizeof buffer2, farq) != NULL)
if(sscanf(buffer2, farq_format_str, A, B)==2){
A[sizeof A - 1]=B[sizeof B - 1]='\0';
Especie (M, A, B);
}


5) Quando você nota que um dos arquivos não pôde ser aberto, você imprime uma mensagem de aviso, mas não para o programa. Deveria pará-lo, pois não faz sentido prosseguir tentando operar com um ponteiro de arquivo que não terá um valor válido.

6) A função Especie() vai gravar sempre na primeira linha da matriz M, já que toda vez em que a função é chamada os valores de nA, ..., nJ são zerados.

Você poderia evitar esse reset declarando essas variáveis como static int, em vez de apenas int. Mesmo assim, acho que o resultado ainda ficaria meio esdrúxulo, pois os valores de nA, ..., nJ andariam com velocidades diferentes, e possivelmente existiriam vários casos de strings diferentes gravadas na mesma linha em diferentes momentos.

Eu não posso dizer como consertar a função porque não consegui saber o que ela deveria realmente fazer.

7) Num comentário dentro da função LimpaMatriz(), você diz que 32 é o código ASCII para nulo. Isso é falso. O código de nulo é justamente nulo (ou seja: zero). 32 é o código do espaço em branco.

Eu não sei se, no fim das contas, você queria o espaço ou o nulo (mais comentários sobre isso abaixo). Em qualquer dos casos, é mais adequado usar o próprio caráter entre apóstrofos (por exemplo: '\0' ou ' ') do que o seu suposto valor numérico. Imagine a situação de levar o programa para uma máquina que não usa ASCII (mainframes da IBM, por exemplo). Um espaço será sempre um espaço, seja seu valor numérico 32 (ASCII), 64 (EBCDIC), ou qualquer outra coisa ainda mais exótica.

8) Eu não consegui saber se você quer tratar M como matriz de caracteres ou como vetor de strings.

Por causa da forma como as duas coisas são declaradas (array bidimensional de caracteres), alguém poderia pensar que dá na mesma, mas a semelhança termina na declaração, pois a forma de trabalhar com as duas coisas é bastante diferente.

A forma como você construiu as funções LimpaMatriz() e ImprimeMatriz() dá a entender que você queria um mapa 2D de caracteres. Especificamente, o fato de você “limpar” todos os elementos de M com espaços caracteriza que você não quer ter strings, já que uma string, para todas as funções padronizadas do C, exige a presença de um byte nulo para indicar onde a string termina. Isso se confirma na hora de imprimir, pois você faz questão de imprimir caráter a caráter.

Por outro lado, quando você chama Especie(), você coloca strings em M, pois strcpy() copia inclusive o byte nulo. Isso, por sua vez, se torna um problema para a função que vai imprimir, pois como ela imprime caráter a caráter, você vai jogar o byte nulo na saída. Esse byte pode comprometer a capacidade de alguns programas de tratar aquela linha, especialmente se forem escritos em C e usarem internamente funções que entendem que o caráter nulo termina a string.


3. Re: Problema no código [RESOLVIDO]

Andre Ribeiro da Costa
andr3ribeiro

(usa Arch Linux)

Enviado em 29/06/2015 - 16:16h

Paulo, se vc não é professor, pense na idéia!


4. Re: Problema no código [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 29/06/2015 - 17:54h

andr3ribeiro escreveu:

Paulo, se vc não é professor, pense na idéia!


+1

Sempre que me deparo com um post do Paulo, paro para ler pois sempre aprendo algo(Obrigado).


--------------------------------------------
povo@brasil ~$ sudo su -
root@brasil ~# find / -iname corrupção -exec rm -rfv {}\ ;



5. Re: Problema no código [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 29/06/2015 - 19:20h

andr3ribeiro escreveu:

Paulo, se vc não é professor, pense na idéia!


Minha sala de aula é aqui, ajudando pontualmente quem quiser ser ajudado. Por outro lado, acho detestável quando aparece um marmanjo querendo tudo de mão beijada.

Até por isso, acho que não teria paciência para uma sala de aula de verdade. Não a tinha nem quando eu era aluno!


6. Re: Problema no código [RESOLVIDO]

Ricardo
r_mil

(usa Ubuntu)

Enviado em 29/06/2015 - 20:39h


Oi Paulo, obrigado pelas dicas, vou melhorar minha implementação com seus conselhos!
Consegui resolver o problema do código... agora sigo em frente para terminá-lo.

Muito obrigado mesmo!
Vlw!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts