Search code examples
cfgetsscanf

Variance in data in/out of loop


I'm trying to write a program that reads in a .pdb file, which is a file type used in biology applications. This type of file has a standard format with varying white space between data. The file is of the form

ATOM      4  N   ALA     1       2.670   1.801   1.072  0.00  0.00
ATOM      5  CA  ALA     1       3.225   3.144   1.197  0.00  0.00
ATOM      6  C   ALA     1       4.408   3.341   0.256  0.00  0.00
ATOM      7  O   ALA     1       4.553   4.394  -0.363  0.00  0.00
....      .  ..  ...     .       .....   .....   .....  ..... ....

So my program (probably poorly written) defines a structure, reads in the data (which I stole from another post here http://www.daniweb.com/software-development/c/threads/65455/reading-a-file-using-fscanf#), and stores it into an indexed struct. Now if I print the values inside of the inner if-loop, it spits out the correct data. However, when I print out the same values outside the outer while-loop, it gives me the wrong atom[].name (which just so happens to be HA, the last value in the 3rd column of data in the input file. All other values are correct.

Here is my program

#include <stdio.h>

typedef struct 
{
   char *atm;
   int serial;
   char *name;
   char *resName;
   int resSeq;
   double x;
   double y;
   double z;
   double occupancy;
   double tempFactor;
} pdb;

int main(int argc, char** argv)
{
   int i, j;
   pdb atom[28]; 
   char atm[5]; 
   char name[3]; 
   char resName[4];
   int serial; 
   int resSeq;
   double x;
   double y;
   double z;
   double occupancy;
   double tempFactor;
   char buff[BUFSIZ];
   FILE *file = fopen("test.pdb", "r");

   i = 0;
   while (fgets(buff, sizeof buff, file) != NULL) 
   {
      if (sscanf(buff, "%s %d %s %s %d %lf %lf %lf %lf %lf", 
         atm, &serial, name, resName, &resSeq, &x, &y, &z, 
         &occupancy, &tempFactor) == 10)
      {
         atom[i].atm = atm;
         atom[i].serial = serial;
         atom[i].name = name;
         atom[i].resName = resName;
         atom[i].resSeq = resSeq;
         atom[i].x = x;
         atom[i].y = y;
         atom[i].z = z;
         atom[i].occupancy = occupancy;
         atom[i].tempFactor = tempFactor;
         i++;
         /*printf("%s ", atom[i].atm);
         printf("%d ", atom[i].serial);
         printf("%s ", atom[i].name);
         printf("%s ", atom[i].resName);
         printf("%d ", atom[i].resSeq);
         printf("%lf ", atom[i].x);
         printf("%lf ", atom[i].y);
         printf("%lf ", atom[i].z);
         printf("%lf ", atom[i].occupancy);
         printf("%lf\n", atom[i].tempFactor);*/
      }
   }
   fclose(file);
   for (j = 0; j < i; j++)
      printf("%d of %d: %s\n", j, i-1, atom[j].name);

   return(0);
}

Any idea why this might be happening? In addition, any help on the program format/structure would also be appreciated. I'm more of a Fortran guy, so C structures are out of my realm of expertise.

Thanks in advance.

EDIT: jsn helped me out and Randy Howard refined it. Here is the updated and working program:

#include <stdio.h>
#include <stdlib.h>

typedef struct 
{
   char *atm;
   int serial;
   char *name;
   char *resName;
   int resSeq;
   double x;
   double y;
   double z;
   double occupancy;
   double tempFactor;
} pdb;

int main(int argc, char** argv)
{
   int i, j;
   pdb atom[28]; 
   int serial; 
   int resSeq;
   double x;
   double y;
   double z;
   double occupancy;
   double tempFactor;
   char buff[BUFSIZ];
   FILE *file = fopen("test.pdb", "r");

   i = 0;
   while (fgets(buff, sizeof buff, file) != NULL) 
   {
      char *atm = malloc(sizeof(char) * 5); 
      char *name = malloc(sizeof(char) * 3); 
      char *resName = malloc(sizeof(char) * 4); 

      if (sscanf(buff, "%s %d %s %s %d %lf %lf %lf %lf %lf", 
         atm, &serial, name, resName, &resSeq, &x, &y, &z, 
         &occupancy, &tempFactor) == 10)
      {
         atom[i].atm = atm;
         atom[i].serial = serial;
         atom[i].name = name;
         atom[i].resName = resName;
         atom[i].resSeq = resSeq;
         atom[i].x = x;
         atom[i].y = y;
         atom[i].z = z;
         atom[i].occupancy = occupancy;
         atom[i].tempFactor = tempFactor;
         i++;
      }
   }
   fclose(file);
   for (j = 0; j < i; j++)
   {
         printf("%s ", atom[j].atm);
         printf("%d ", atom[j].serial);
         printf("%s ", atom[j].name);
         printf("%s ", atom[j].resName);
         printf("%d ", atom[j].resSeq);
         printf("%lf ", atom[j].x);
         printf("%lf ", atom[j].y);
         printf("%lf ", atom[j].z);
         printf("%lf ", atom[j].occupancy);
         printf("%lf\n", atom[j].tempFactor);
   }

   return(0);
}

Solution

  • Inside the while loop you need to allocate new memory for each char* for each name. You are overwriting them right now.

    while (fgets(buff, sizeof buff, file) != NULL) 
    {
    
        char *atm = malloc(sizeof(char) * 5); 
        char *name = malloc(sizeof(char) * 3); 
        char *resName = malloc(sizeof(char) * 4); 
    
        if (sscanf(buff, "%s %d %s %s %d %lf %lf %lf %lf %lf", 
                 atm, &serial, name, resName, &resSeq, &x, &y, &z, 
                 &occupancy, &tempFactor) == 10)
    

    You are copying the char array (pointers), so all the names should be the same (the last entry).