Reputation: 11
I'm somewhat new to C (and also to this website) and had a question about a project I'm working on. The part I'm stuck on is file I/O stuff, since I never learned that part in my previous class. So, the input (.txt) file looks like this:
REGISTERS
R5 11
R7 15
R21 4
MEMORY
4 12
12 92
[OTHER STUFF]
The file gives us arbitrary register numbers, followed by its contents (R0 to 31, Regs with value 0 omitted). The next section gives memory locations in a similar format, followed by the contents again. Then more sections that I can deal with later.
My question is: how do I read in the register/memory locations and their values? Currently I have a size 32 array to store the contents in and a variable for the Reg number and value (Example: Registers[regNumber] = regValue;), but I don't know how to start properly reading values after the word REGISTERS.
What I've tried:
I did some looking around and I managed to read in a test input file with only one line: "R5 11". For this I did:
fscanf(file_ptr, "R%d %d", ®Number, ®Value);
I printed the variables regNumber, regValue, and they properly gave me 5 and 11. But how do I do this for any number of lines after the word REGISTERS and stop at the word MEMORY?
I'm assuming whatever the solution is, I can do the same thing to read in the MEMORY locations and values.
Upvotes: 0
Views: 518
Reputation: 13580
I wouldn't use fscanf
here, because you cannot look ahead and see if the next
line is REGISTERS
or MEMORY
or whatever. In theory you could check the
return value of fscanf
but the code would get very messy very quickly.
A better solution is to use fgets
to fetch a whole line. If the line is equal
to REGISTERS\n
, then you know the next lines are register lines, etc. The
parsing itself can be done with sscanf
, which is like fscanf
but instead of
reading from a FILE*
buffer, it reads from a string.
So, I'd do:
enum mode_t {
REGISTERS = 0,
MEMORY,
...,
INVALID
};
int parse_file(FILE *file_ptr, int *registers, int some_other_params)
{
if(file_ptr == NULL || registers == NULL)
return 0;
char line[1024];
enum mode_t mode = INVALID;
while(fgets(line, sizeof line, file_ptr))
{
if(strcmp(line, "REGISTERS\n") == 0)
{
mode = REGISTERS;
continue;
} else if(strcmp(line, "MEMORY\n") == 0) {
mode = MEMORY;
continue;
} // keep adding if(strcmp.... as needed)
if(mode == INVALID)
{
fprintf(stderr, "Invalid file format\n");
return 0; // or a better error handling
}
int ret;
switch(mode)
{
case REGISTERS:
ret = parse_registers(line, registers);
break;
case MEMORY:
ret = parse_memory(line, some_other_args);
break;
...
}
// evaluate ret and do error handling if necessary
}
return 1;
}
Using mode
for the parsing state, you can determine which section you are
parsing. Then you have to write functions like parse_registers
and
parse_memory
that do the real parsing of the lines, for example:
int parse_registers(const char *line, int *registers)
{
if(line == NULL || registers == NULL)
return 0;
int regNumber, regValue;
if(sscanf("R%d %d", ®Number, ®Value) != 2)
{
fprintf(stderr, "Invalid format for REGISTER section\n");
return 0;
}
if(regNumber < 0 || regNumber > 31)
{
fprintf(stderr, "Invalid register number %d\n", regNumber);
return 0;
}
registers[regNumber] = regValue;
return 1;
}
Now you can implement parse_memory
and the other parse_xxx
functions for the
other sections of the file.
Upvotes: 2
Reputation: 685
You can read in a full line of the text file with fgets(), then strcmp or strncmp the line for the "REGISTERS" and "MEMORY" string literals.
The way your input.txt is formatted everything after "REGISTERS" has the same format up until "MEMORY", and everything after "MEMORY" is the same format. Maybe something like the following would work, although there are probably better alternatives:
int main () {
FILE *fp;
char str[60];
int input_type = 0; //you can enum this for the input types you have
int line_num = 0;
/* opening file for reading */
fp = fopen("input.txt" , "r");
if(fp == NULL) {
perror("Error opening file");
return(-1);
}
while( fgets (str, 60, fp)!=NULL ) { // dont really need the != NULL compare here.
line_num++;
/* Parse the next line in the file */
if(strcmp(str,"MEMORY") == 0){
//the next lines will be formatted like memory input lines
input_type = 2; //This should be an enum or macro
continue;
}
if(strcmp(str,"REGISTERS") == 0){
//the next lines will be formatted like register input lines
input_type = 1;//Also should be enum or macro
continue;
}
switch(input_type){
case 0:
fprintf(stderr,"Failed to find REGISTER keyword%d\n",line_num);
break;
case 1:
sscanf(str, "R%d %d", ®Number, ®Value); //parse known string format
do_something(®Number, ®Value);
break;
case 2:
sscanf(str, "%d %d", &memNumber, &memValue); //parse known string format
do_something_else(&memNumber, &memValue);
break;
default:
fprintf(stderr,"Could not find input format at line %d\n",line_num);
break;
}
}
fclose(fp);
return(0);
}
Upvotes: 2