While you would generally use the C++ iostream
library for file I/O, there is nothing that says you can't use the C cstdio
functions such as fscanf
-- as long as you use them correctly (and often they will be faster than the iostream
approach)
In your case, you have a lot of numbers with some text in the middle that you are attempting to read with fscanf
in a loop. That's fine, that's simple enough to do, but ... you must correctly handle the matching failure case which will occur when you attempt to read 's'
with the "%08x"
conversion specifier.
When a matching failure occurs, character extraction from the stream stops at the point of failure, leaving everything beginning with the character causing the failure (and what follows it) unread in the input buffer. Unless you properly extract the characters causing the matching failure from the input stream, you will likely encounter an endless loop as the characters causing the failure remain unread, just waiting to bite you again on the next attempted read.
So, how to handle the matching failure? The cctype
header provides the isdigit
macro that allows you to simply test if the next character in the input stream is a digit. You test the character by first reading with fgetc
(or getc
- same thing but often implemented as a macro) and then testing with isdigit
, e.g.
int c = fgetc(in_file); /* read next char */
while (c != EOF && !isdigit(c)) /* check EOF and isdigit */
c = fgetc(in_file); /* get next char */
Above you read the next character, then enter a loop validating you haven't reached EOF
and then checking if c
is Not a digit. If those conditions are met, you get the next character a check again, until you reach EOF
Or you find the next digit in the input stream. But now you have a problem, you have already read the digit from the stream, how is fscanf
going to be able to read it as part of the next integer?
Simple -- put it back in the input stream:
if (c != EOF) /* if not EOF, then digit */
ungetc (c, in_file); /* put back for next read */
Now you are in a position to read all 64 integer values from in_file
with a simple loop, e.g.
while (1) { /* loop continually until EOF */
int rtn = fscanf (in_file,"%08x", &number1); /* validate return */
if (rtn == EOF) /* if EOF, break loop */
break;
else if (rtn == 0) { /* handle matching failure */
int c = fgetc(in_file); /* read next char */
while (c != EOF && !isdigit(c)) /* check EOF and isdigit */
c = fgetc(in_file); /* get next char */
if (c != EOF) /* if not EOF, then digit */
ungetc (c, in_file); /* put back for next read */
}
else /* good read, output number */
fprintf (out_file, "%08x\n", number1);
}
(note: your output file has been renamed from in_file1
to out_file
-- always use meaningful variable names)
Now some clean up. When you open in_file
, you validate the file is open for reading. Fine, but for the error condition you exit (-1);
. Don't return negative values to the shell. You have two constants to indicate success/failure names EXIT_SUCCESS
(0
) and EXIT_FAILURE
(value 1
, Not -1
).
While you did check that in_file
was open for reading, you wholly failed to check if your output file was open for writing? Always validate the return of all input/output stream and I/O functions. Otherwise attempting to write to an stream in an error state invokes Undefined Behavior.
Putting it altogether, you could do:
#include <cstdio>
#include <cstdlib>
#include <cctype>
using namespace std;
int main (void) {
unsigned int number1;
FILE* in_file = fopen ("example.txt", "r");
FILE* out_file = fopen ("wrte.txt", "w");
if (!in_file) { /* validate file open for reading */
printf ("oops, file can't be read\n");
exit (1); /* don't return negative values to the shell */
}
if (!out_file) { /* validate file open for writing */
printf ("oops, file can't be read\n");
exit (1); /* don't return negative values to the shell */
}
while (1) { /* loop continually until EOF */
int rtn = fscanf (in_file,"%08x", &number1); /* validate return */
if (rtn == EOF) /* if EOF, break loop */
break;
else if (rtn == 0) { /* handle matching failure */
int c = fgetc(in_file); /* read next char */
while (c != EOF && !isdigit(c)) /* check EOF and isdigit */
c = fgetc(in_file); /* get next char */
if (c != EOF) /* if not EOF, then digit */
ungetc (c, in_file); /* put back for next read */
}
else /* good read, output number */
fprintf (out_file, "%08x\n", number1);
}
fclose (in_file);
fclose (out_file);
}
Example Output File
$ cat wrte.txt
01000000
01000000
01000000
01000000
...
01000000
All 64 values are written which you can confirm with wc -l
, e.g.
$ wc -l < wrte.txt
64
Look things over and let me know if you have further questions. The same logic would apply if you were using the iostream
library, the function names are slightly different (some identical) but are implemented as member-functions instead.