David Butler
David Butler

Reputation: 25

Reading a file, and creating a string array from temporary variables

The ultimate goal is creating an array of strings from a file. So I have a file that I need to get temporary variables from temp1, temp2, temp3, etc. I wont know how many are in advance. My first thought was to create a normal string from the variables I do find using strcat then splitting that using tokens. I thought I could bypass this by just adding the string normally.

while(feof(inputFile))
{ 
    fscanf(inputFile, " %s ", readIn);

    if(strcmp("AND", readIn) == 0 || strcmp("OR", readIn) == 0 || strcmp("NAND", readIn) == 0 || strcmp("NOR", readIn) == 0 || strcmp("XOR", readIn) == 0)
    {
        fscanf(inputFile,  "%s ", readIn);
        fscanf(inputFile,  "%s ", readIn);
        fscanf(inputFile, "%s ", tempVar);
        //printf("the char is %c", tempVar[0]);

        //check to see if  its a temp variable since temps are always lower case
        if(tempVar[0] == 't')
        {
            numVars++;
            tempHeaders = realloc(tempHeaders, sizeof(char*) * numVars);
            int temp = numVars -1;
            printf("the variable is %s and the number is %d\n", tempVar, temp);
            tempHeaders[numVars - 1] = tempVar;
        }                       

        numGates++;
    }
}

At this point the number of variables are right, the issue is when adding to the array tempHeaders. the entire value of the array will be whatever the last word of the file is, i.e. if the lastword is out. tempHeaders i - n would all be out. Any ideas? Thank you

I've edited out feof from the while loop and changed to "while(fscanf(inputFile, "%s ", readIn) != EOF){"

sample input would be

OR IN1 IN2 temp1
OR IN3 IN4 temp2
OR IN5 IN6 temp3
OR IN7 IN8 temp4
AND temp1 temp2 temp5
AND temp3 temp4 temp6
XOR temp2 temp6 OUT1

sample output at this time

the variable is temp1 and the number is 0
the variable is temp2 and the number is 1
the variable is temp3 and the number is 2
the variable is temp4 and the number is 3
the variable is temp5 and the number is 4
the variable is temp6 and the number is 5
Printing OUT1 
Printing OUT1 
Printing OUT1 
Printing OUT1 
Printing OUT1 
Printing OUT1 
numGates = 7, numVars = 6 

the prints should be temp1, temp2, temp3, etc...

Upvotes: 0

Views: 337

Answers (1)

Scheff's Cat
Scheff's Cat

Reputation: 20141

I made a working sample to do what I believe you intended to do:

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

char* strdup(const char *str)
{
  int len = strlen(str);
  char *str2 = malloc((len + 1) * sizeof (char));
  if (str2) strcpy(str2, str);
  return str2;
}

int main()
{
  FILE *f = stdin;
  /** @todo evaluation of command line arguments could
   * check whether input file given
   * and in that case override f with fopen().
   */
  /* read input */
  if (!f) {
    fprintf(stderr, "ERROR: Cannot read input!\n");
    return 1;
  }
  int nGates = 0, nVars = 0;
  char **vars = NULL;
  char buffer[80];
  for (int iLine = 1; fgets(buffer, sizeof buffer, f); ++iLine) {
    char *op = strtok(buffer, " \t\r\n");
    if (!op
      || strcmp(op, "AND") != 0 && strcmp(op, "OR") != 0 && strcmp(op, "XOR") != 0
      && strcmp(op, "NAND") != 0 && strcmp(op, "NOR") != 0) {
      fprintf(stderr, "ERROR in line %d: OP expected!\n", iLine);
      continue;
    }
    char *var;
    while (var = strtok(NULL, " \t\r\n")) {
      if (var[0] == 't') {
        ++nVars;
        vars = realloc(vars, sizeof (char*) * nVars);
        if (!vars) {
          fprintf(stderr, "ERROR: Out of memory!\n");
          return 1;
        }
        int iVar = nVars - 1;
        vars[iVar] = strdup(var);
        if (!vars[iVar]) {
          fprintf(stderr, "ERROR: Out of memory!\n");
          return 1;
        }
        printf("Var. #%d: '%s'\n", iVar, var);
      }
    }
    ++nGates;
  }
  /* evaluate input */
  printf("Report:\n");
  printf("nGates: %d, nVars: %d\n", nGates, nVars);
  for (int i = 0; i < nVars; ++i) {
    printf("vars[%d]: '%s'\n", i, vars[i]);
  }
  /* done */
  return 0;
}

I tested the code in bash on cygwin (on Windows 10):

$ gcc --version
gcc (GCC) 6.4.0

$ gcc -std=c11 -o testStrtok testStrtok.c

$ ./testStrtok <<'EOF'
OR IN1 IN2 temp1
OR IN3 IN4 temp2
OR IN5 IN6 temp3
OR IN7 IN8 temp4
AND temp1 temp2 temp5
AND temp3 temp4 temp6
XOR temp2 temp6 OUT1
EOF
Var. #0: 'temp1'
Var. #1: 'temp2'
Var. #2: 'temp3'
Var. #3: 'temp4'
Var. #4: 'temp1'
Var. #5: 'temp2'
Var. #6: 'temp5'
Var. #7: 'temp3'
Var. #8: 'temp4'
Var. #9: 'temp6'
Var. #10: 'temp2'
Var. #11: 'temp6'
Report:
nGates: 7, nVars: 12
vars[0]: 'temp1'
vars[1]: 'temp2'
vars[2]: 'temp3'
vars[3]: 'temp4'
vars[4]: 'temp1'
vars[5]: 'temp2'
vars[6]: 'temp5'
vars[7]: 'temp3'
vars[8]: 'temp4'
vars[9]: 'temp6'
vars[10]: 'temp2'
vars[11]: 'temp6'

$ 

Notes:

  1. I found a realloc() in OP's code to resize the array for variables (tempHeader in OP, vars in mine). What I didn't find – allocation of memory for variable names itself. I used strdup() for that.

  2. I could swear I've always used strdup() in the past (with #include <string.h> for the proper prototype). However, gcc complained and I found:

    As all functions from Dynamic Memory TR, strdup is only guaranteed to be available if __STDC_ALLOC_LIB__ is defined by the implementation and if the user defines __STDC_WANT_LIB_EXT2__ to the integer constant 1 before including string.h.

    in cppreference.com
    I just made my own version – it's not too complicated.

  3. strtok() is such a function... Hmmm. It's very old and has limitations which makes it dangerous under certain circumstances. (E.g. you shouldn't use it in threads.) However, these concerns aside, it does perfectly what is needed (and there is no multi-threading in this code).

  4. I used a fix-size input buffer char buffer[80]; to keep the sample short. In a productive project, I certainly would do something more complicated to be able to read lines of "arbitrary" length – and I certainly would need much more code for this.

  5. In the expected output of the OP, there are no duplicates of variables. However, the code sample does not provide any code which tries to prevent it. Thus, I didn't as well. To do this, the following code had to be inserted (before the one and only occurrence of ++nVars;):

    int found = 0;
    for (int i = 0; i < nVars; ++i) {
      if (found = (strcmp(var, vars[i]) == 0)) break;
    }
    if (found) continue; /* continues the while (var = strtok(NULL, " \t\r\n")) loop */
  6. The provided code does not free() the allocated memory. As is, this is not a problem – the OS will make this job after leaving main(). However, a "clean" solution would be to clean the vars explicitly by:

    /* clean-up */
    for (int i = 0; i < nVars; ++i) free(vars[i]);
    free(vars);

With the recommended change of 5., the output is:

$ gcc -std=c11 -o testStrtok testStrtok.c

$ ./testStrtok <<'EOF'
OR IN1 IN2 temp1
OR IN3 IN4 temp2
OR IN5 IN6 temp3
OR IN7 IN8 temp4
AND temp1 temp2 temp5
AND temp3 temp4 temp6
XOR temp2 temp6 OUT1
EOF
Var. #0: 'temp1'
Var. #1: 'temp2'
Var. #2: 'temp3'
Var. #3: 'temp4'
Var. #4: 'temp5'
Var. #5: 'temp6'
Report:
nGates: 7, nVars: 6
vars[0]: 'temp1'
vars[1]: 'temp2'
vars[2]: 'temp3'
vars[3]: 'temp4'
vars[4]: 'temp5'
vars[5]: 'temp6'

$

Live demo on ideone

Upvotes: 1

Related Questions