There are a whole lot of subtle misuses of syntax, etc. throughout your code. I've tried to identify all and note in comments the nature of the problem.
From an overall standpoint, but biggest shortcomings were not validating your file operations. You must validate a fopen
succeeded, --- Before you attempt to read characters from it. The same goes for fclose
following a write to the file to insure no stream errors are present on closer that would leave characters unwritten to the file. (If you are just reading from a files, then no validation on close is generally warranted.)
When declaring character arrays to hold strings, you should initialize all elements with 0
(effectively filling the array with nul-terminating characters to insure your string will always be nul-terminated as long as you do not write beyond the end of your array. It is simple and short. Something as simple as char str[10] = "", st1[10] = "";
is all you need. (by convention, if you fill the first element of an array or struct with a value, all others are initialized to zero
by default.)
There are other subtleties. You mix getc
and fgetc
throughout. While that will work fine, just be aware that getc
is generally implemented as a Macro and doesn't guarantee single-pass operation where fgetc
does.
That's pretty much the overview, go through the code, and open your code up. You may think it is readable all crammed together, but if you think about a blank line between blocks in code, the same way paragraphs in writing, it sure makes the flow easier to follow in both.
Putting that together, you could do something like the following:
Note: edited per your comment, corrected read logic errors not addressed originally.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* use constants, not 'magic numbers' in code */
enum { MAXC = 10, MAXND = 100, MAXFN = 4096 };
void keyword (char *str)
{
if (strncmp ("for", str, strlen ("for")) == 0 ||
strncmp ("while", str, strlen ("while")) == 0 ||
strncmp ("do", str, strlen ("do")) == 0 ||
strncmp ("int", str, strlen ("int")) == 0 ||
strncmp ("float", str, strlen ("float")) == 0 ||
strncmp ("char", str, strlen ("char")) == 0 ||
strncmp ("double", str, strlen ("double")) == 0 ||
strncmp ("static", str, strlen ("static")) == 0 ||
strncmp ("switch", str, strlen ("switch")) == 0 ||
strncmp ("case", str, strlen ("case")) == 0)
printf (" keyword : %s\n", str);
else
printf (" identifier : %s\n", str);
}
int main (void) /* main() is type int, and returns a value */
{
FILE *f1, *f2, *f3;
char str[MAXC] = "", st1[MAXFN] = ""; /* initialize arrays 0 */
int c, num[MAXND] = {0}, lineo = 0, tokenvalue = 0, i = 0, j = 0, k = 0;
printf ("\nEnter the c program: ");
if (!fgets (st1, 10, stdin)) {
fprintf (stderr, "error: invalid input 'st1'\n");
return 1;
}
if (!(f2 = fopen ("input", "w"))) { /* validate opening */
fprintf (stderr, "error: file open failed 'input'.\n");
return 1;
}
/* The while takes input from stdin and writes to f1, but has nothing
* to do with the file you opened "input". I'm guessing you want to
* read file stream f1 (copy it) into "input" so you can reopen it
* in "r" mode and check for identifiers, keywords, etc.
*
* You cannot redirect a file to your code, and then prompt for
* a filename -- fgets will take the code as your filename
* (or as much will fit) because stdin is FIFO, not LIFO
*
while ((c = getchar ()) != '\n' && c != EOF)
fputc (c, f1);
*/
if (!(f1 = fopen (st1, "r"))) { /* validate opening */
fprintf (stderr, "error: file open failed '%s'.\n", st1);
return 1;
}
while ((c = fgetc (f1)) != EOF) /* you should really do this with fgets */
fputc (c, f2); /* and read/write a line at a time */
fclose (f1);
if (fclose (f2)) { /* validate close after *write*, stream error */
fprintf (stderr, "error: on stream close after write 'f2'.\n");
return 1;
}
if (!(f1 = fopen ("input", "r"))) { /* validate opening */
fprintf (stderr, "error: invalid input 'input'\n");
return 1;
}
if (!(f2 = fopen ("identifier", "w"))) { /* validate opening */
fprintf (stderr, "error: invalid input 'identifier'\n");
return 1;
}
if (!(f3 = fopen ("specialchar", "w"))) { /* validate opening */
fprintf (stderr, "error: invalid input 'specialchar'\n");
return 1;
}
while ((c = fgetc (f1)) != EOF) {
if (isdigit (c)) {
tokenvalue = c - '0';
c = fgetc (f1); /* fgetc guarantees single evaluation */
while (isdigit (c)) {
tokenvalue *= 10 + c - '0';
c = fgetc (f1);
}
num[i++] = tokenvalue;
ungetc (c, f1);
} else if (isalpha (c)) {
fputc (c, f2);
if ((c = fgetc (f1)) && c != EOF) /* need () around assignment */
while (isdigit (c) || isalpha (c) || c == '_' || c == '$') {
putc (c, f2);
c = fgetc (f1);
}
fputc (c, f2);
ungetc (c, f1);
} else if (c == ' ' || c == '\t') /* one 'space' for a char */
putchar (' '); /* printing empty-char ?, looks like 'space' or 0 */
else if (c == '\n')
lineo++;
else
putc (c, f3);
}
if (fclose (f2)) { /* validate close after *write*, stream error */
fprintf (stderr, "error: on stream close after write 'f2'.\n");
return 1;
}
if (fclose (f3)) { /* validate close after *write*, stream error */
fprintf (stderr, "error: on stream close after write 'f3'.\n");
return 1;
}
fclose (f1);
printf ("\nThe nm. in the program are: ");
for (j = 0; j < i; j++)
printf ("%d", num[j]);
putchar ('\n'); /* no need for printf for a single 'char' */
if (!(f2 = fopen ("identifier", "r"))) { /* validate opening */
fprintf (stderr, "error: invalid input 'identifier'\n");
return 1;
}
k = 0;
printf ("The keywords and identifiers are: ");
while ((c = fgetc (f2)) != EOF) {
if (k + 1 < MAXC && c != '\0') /* you must limit chars to str len */
str[k++] = c; /* and your logic needs a rework */
else {
str[k] = 0;
keyword (str);
k = 0;
}
}
putchar ('\n');
fclose (f2);
if (!(f3 = fopen ("specialchar", "r"))) { /* validate opening */
fprintf (stderr, "error: invalid input 'specialchar'\n");
return 1;
}
printf ("\nSpecial Characters are: ");
while ((c = getc (f3)) != EOF)
printf ("%c", c);
putchar ('\n');
fclose (f3);
printf ("Total no. of lines are: %d\n", lineo);
}
I have run a short C file through to test you logic. You still have some work to do. Compile it and run your data and let me know if you have any additional questions. An example showing where you still have work to do is:
Input File
$ nl -ba whileit.c
1 #include <stdio.h>
2 #include <string.h>
3
4 int main (void) {
5
6 char a[] = "You are welcome",
7 b[5][20] = {{0}},
8 *pch;
9 int i = 0;
10
11 pch = strtok ( a," \t" );
12
13 while (i < 5 && pch) {
14 strcpy (b[i++], pch);
15 pch = strtok( NULL, " \t\n" );
16 }
17
18 i = 0;
19 while (*b[i]) {
20 printf( "b[%d] = %s\n", i, b[i] );
21 i++;
22 }
23
24 return 0;
25 }
Example Use/Output
$ ./bin/fopenprob
Enter the c program: whileit.c
The nm. in the program are: 52000500 // nm is correct
The keywords and identifiers are: identifier : include s
identifier : dio.h>inc
identifier : ude strin
identifier : .h>int ma
identifier : n void)ch
identifier : r a[You a
identifier : e welcome
identifier : b[pch;int
identifier : i pch str // keyword logic needs work
identifier : ok a,t"wh
identifier : le i pch)
identifier : trcpy b[i
identifier : pch)pch s
identifier : rtok(NULL
identifier : t\n"i whi
identifier : e b[i]pri
identifier : tf(b[d]s\
identifier : "i,b[i]i+
Special Characters are: #<.>#<.>(){[]="",[][]={{}},*;=;=(,"\");(<&&){([++],);=(,"\\");}=;(*[]){("[%]=%\",,[]);++;};}
Total no. of lines are: 25
files written
$ nl -ba input
1 #include <stdio.h>
2 #include <string.h>
3
4 int main (void) {
5
6 char a[] = "You are welcome",
7 b[5][20] = {{0}},
8 *pch;
9 int i = 0;
10
11 pch = strtok ( a," \t" );
12
13 while (i < 5 && pch) {
14 strcpy (b[i++], pch);
15 pch = strtok( NULL, " \t\n" );
16 }
17
18 i = 0;
19 while (*b[i]) {
20 printf( "b[%d] = %s\n", i, b[i] );
21 i++;
22 }
23
24 return 0;
25 }
(Manual line breaks at 79 chars for readability here - none in file:)
$ cat identifier
include stdio.h>include string.h>int main void)char a[You are welcome"b[pch;int
i pch strtok a,t"while i pch)strcpy b[i+pch)pch strtok(NULL,t\n"i while b[i]pri
ntf(b[d]s\n"i,b[i]i+return
$ cat specialchar
#<.>#<.>(){[]="",[][]={{}},*;=;=(,"\");(<&&){([++],);=(,"\\");}=;(*[]){("[%]=%\
",,[]);++;};}
As you can see, your keyword and identifier logic needs work. Hint, to identify keyword and identifiers you must insure you send only strings beginning with identifiers to your keyword
function. I have modified your keyword
function to use strncmp
to test for keywords, so you can send e.g. "for(i=0"
to keyword
and it will identify "for"
, but you must insure str
starts with the keyword. I have also left additional comments in your code for improvements. This should be more than enough to get you going without 'doing it for you' :)