Question

I have a question about reading a string containing spaces from a file. Here is my function that reads data from file and writes it to array of structures. However, if the input for the first string contains two strings and space between them, then the first string goes to the first parameter and the second string goes to the second parameter. How to split the line from a file in such a way that necessary data goes to specified fields of structure? Thank you!

void readFileToDiary(FILE* file, drivingDiary* diaryTemp){

    int i = 0;

    file = fopen("test.txt", "r");
    if (file == NULL)
        printf("\nError opening file!\n");
    else{
        while (!feof(file)){
            fscanf(file, "%s %s %d %s %s %d", diaryTemp[i].locationStart,
                diaryTemp[i].timeStart, &diaryTemp[i].odometerStart,
                diaryTemp[i].locationEnd, diaryTemp[i].timeEnd,
                &diaryTemp[i].odometerEnd);
            i++;
        }
    }
}

Example line inside the file contains: some place 13:40 10000 some other place 14:45 10120

So,

      "some place" -> first field of structure,
             13:40 -> second field,
             10000 -> third field,
"some other place" -> fourth field,
             14:45 -> fifth filed,
             10120 -> sixth field.

UPD Working version is here (without fscanf() check)!

void readFileToDiary(FILE* file, drivingDiary* diary){

    int i = 0;

    file = fopen("test.txt", "r");
    if (file == NULL)
        printf("\nError opening file!\n");
    else{
        while(fscanf(file, " %[a-zA-Z ]%[0-9:]%d %[a-zA-Z ]%[0-9:]%d%*[\n]",
                diary[i].locationStart,
                diary[i].timeStart,
                &diary[i].odometerStart,
                diary[i].locationEnd,
                diary[i].timeEnd,
                &diary[i].odometerEnd) != EOF)
            i++;        
        }
    }
}
Was it helpful?

Solution

Since the number of spaces in the start and end locations is unknown, will be complex using a scanf() method. However, it can be parsed using a more 'old-school' method....

"some place 13:40 10000 some other place 14:45 10120"

First, think of each line as containing two 'way-points'; a 'start', and and 'end'. Then simplify the parsing individual way-points by using a single "way-point" parsing method.

"some place 13:40 10000", "some other place 14:45 10120"

So, there would be several ways to build the 'way-point' parser. As I looked at the problem, I didn't want to start out using a space character as a delimiter. I wanted to find some other starting point.

Each way-point contains a ':' character, so I started there. From the ':' character I went left, to find the space, and converted the space to a '\0' string termination character. That isolated the 'location' of the waypoint.

Then, from the ':', I went right, to find the space, and converted the space to a '\0' string termination character. That isolated the 'time' of the waypoint.

The odometer portion of the waypoint is easily isolated using strtoul().

My code follows here:

/***************************************************************************
** Compiler setup
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

typedef struct DRIVING_WAYPOINT_S
   {
   char *location;
   char *time;
   long  odometer;
   } DRIVING_WAYPOINT_T;

typedef struct DRIVING_DIARY_S
   {
   DRIVING_WAYPOINT_T start;
   DRIVING_WAYPOINT_T end;
   } DRIVING_DIARY_T;

/***************************************************************************
** Parse a waypoint.
*/
int ParseRecord(
      char                *I__string,
      char               **_O_string,
      DRIVING_WAYPOINT_T  *I__waypoint
      )
   {
   int rCode=0;
   char *cp, *space;

   /* Parse location */
   cp=strchr(I__string, ':'); /* Find the first ocurrance of ':' */
   if(NULL == cp)
      {
      rCode=EINVAL;
      fprintf(stderr, "Parse error.  Time does not contain a ':'\n");
      goto CLEANUP;
      }

   space = cp;
   while(' ' != *space)
      --space;

   *space = '\0';
   I__waypoint->location = strdup(I__string);

   /* Parse time */
   cp = space + 1;
   space=strchr(cp, ' ');
   if(NULL == space)
      {
      rCode=EINVAL;
      fprintf(stderr, "Parse error.  No space following time\n");
      goto CLEANUP;
      }

   *space='\0';
   I__waypoint->time = strdup(cp);

   /* Parse odometer */
   cp = space+1;
   I__waypoint->odometer = strtol(cp, &cp, 10);
   while(' ' == *cp)
      ++cp;

   if(_O_string)
      *_O_string = cp;

CLEANUP:

   return(rCode);
   }

/*******************************************************************************
** Parse the diary file. 
*/
int ReadFileToDiary(
      FILE             *I__fp,
      DRIVING_DIARY_T **IO_diary,
      int              *IO_diaryEntries
      )
   {
   int rCode = 0;
   char line[255+1];

   for(;;)
      {
      DRIVING_DIARY_T *tmp;
      char *cp;

      /* Read the next line from the file. */
      errno=0;
      if(NULL == fgets(line, sizeof(line), I__fp))
         {
         if(feof(I__fp))
            break;

         rCode=errno;
         fprintf(stderr, "fgets() reports: %d.\n", errno);
         goto CLEANUP;
         }

      /* Expand the diary array for one more entry. */
      tmp=realloc(*IO_diary, ((*IO_diaryEntries)+1) * sizeof(DRIVING_DIARY_T));
      if(NULL == tmp)
         {
         rCode=ENOMEM;
         fprintf(stderr, "realloc() failed.\n");
         goto CLEANUP;
         }
      *IO_diary = tmp;
      memset(&(*IO_diary)[*IO_diaryEntries], '\0', sizeof(DRIVING_DIARY_T));

      /* Check for empty string. */
      if('\0' == *line)
         continue;

      /* Parse the 'start' waypoint. */
      rCode=ParseRecord(line, &cp, &(*IO_diary)[*IO_diaryEntries].start);
      if(rCode)
         {
         fprintf(stderr, "ParseRecord(start) reports: %d\n", rCode);
         goto CLEANUP;
         }

      /* Parse the 'end' waypoint. */
      rCode=ParseRecord(cp, NULL, &(*IO_diary)[*IO_diaryEntries].end);
      if(rCode)
         {
         fprintf(stderr, "ParseRecord(end) reports: %d\n", rCode);
         goto CLEANUP;
         }

      /* Increment the 'diary entries' counter. */
      (*IO_diaryEntries)++;
      }

CLEANUP:

   return(rCode);   
   }

/*******************************************************************************
** Free the diary array.
*/
int DiaryFree(
      DRIVING_DIARY_T *diary,
      int              diaryEntries
      )
   {
   int rCode=0;
   int nCnt;

   for(nCnt=0; nCnt<diaryEntries; ++nCnt)
      {
      free(diary[nCnt].start.location);
      free(diary[nCnt].end.location);
      free(diary[nCnt].start.time);
      free(diary[nCnt].end.time);         
     }

   free(diary);   

   return(rCode);
   }

/*******************************************************************************
** Program start.  
*/
int main()
   {
   int              rCode        = 0;
   FILE            *fp           = NULL;
   DRIVING_DIARY_T *diary        = NULL;
   int              diaryEntries = 0;
   int              nCnt;

   /* Open the data file. */
   errno=0;
   fp = fopen("test.txt", "r");
   if(NULL == fp)
      {
      rCode=errno;   
      fprintf(stderr, "fopen() failed.  errno:%d\n", errno);
      goto CLEANUP;
      }

    /* Parse the file into the dynamic diary array. */
    rCode=ReadFileToDiary(fp, &diary, &diaryEntries);
    if(rCode)
      {
      fprintf(stderr, "ReadFileToDiary() reports: %d\n", rCode);
      goto CLEANUP;
      }

    /* Print out the array. */
    for(nCnt=0; nCnt < diaryEntries; ++nCnt)
      {
      printf("[%d] %s %s %ld %s %s %ld\n",
         nCnt,
         diary[nCnt].start.location,
         diary[nCnt].start.time,
         diary[nCnt].start.odometer,
         diary[nCnt].end.location,
         diary[nCnt].end.time,
         diary[nCnt].end.odometer
         );
       }

CLEANUP:

   if(diary)
      DiaryFree(diary, diaryEntries);

   if(fp)
      fclose(fp);

   return 0;
   }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top