=========================== CBE Pt. 16: Simple File I/O [TOC] =========================== This is some minimalistic introduction to file I/O in C. It is limited to handling text files. Open a File for Reading or Writing ================================== Function __fopen()__ ---- CODE (type=c) ------------------------------------------------------------- FILE *fopen(const char *filename, const char *mode); -------------------------------------------------------------------------------- declared in __ can be used to open a file for reading or writing. Parameter _filename_ is the path to a file (e.g. "foo.txt","../foo.txt", "/usr/include/ar.h"). Using "w" for _mode_ will attempt to open the file for writing and "r" for reading. For example ---- CODE (type=c) ------------------------------------------------------------- FILE *in = fopen("foo.txt", "r"); FILE *out = fopen("bar.txt", "w"); -------------------------------------------------------------------------------- attempts to open file "foo.txt" for reading and file "bar.txt" for writing. On success _fopen()_ returns an _open file pointer_. On failure it returns a null pointer. Opening a file for reading for example fails if the file does not exists. Opening a file for writing can fail if the file is a directory or can not be created for other reasons (e.g. insufficient permissions). Closing a File ============== Before you program terminates you should close the file with __fclose()__: ---- CODE (type=c) ------------------------------------------------------------- int fclose(FILE *stream); -------------------------------------------------------------------------------- :links: fopen\(\) -> https://man7.org/linux/man-pages/man3/fopen.3.html fclose\(\) -> https://man7.org/linux/man-pages/man3/fclose.3.html Operation for Reading and Writing ================================= Function __getline()__ (see the test program for unique strings in __Session 17, Page 3__) --- CODE (type=c) -------------------------------------------------------------- ssize_t getline(char **lineptr, size_t *n, FILE *in); -------------------------------------------------------------------------------- can be used to read from a file by using an open file pointer in reading mode. Function __fprintf()__ ---- CODE (type=c) ------------------------------------------------------------- int fprintf(FILE *out, const char *format, ...); -------------------------------------------------------------------------------- can be used to write to a text file by using an open file pointer in write mode. :links: getline\(\) -> https://man7.org/linux/man-pages/man3/getline.3.html Session 17, Page 3 -> doc:session17/page03 fprintf\(\) -> https://man7.org/linux/man-pages/man3/fprintf.3p.html Generating Enum Constants ========================= Consider this text file for some token kinds ---- CODE (file=session20/fileio/tokenkind.txt) -------------------------------- EOI BAD_TOKEN HEX_LITERAL OCT_LITERAL DEC_LITERAL PLUS MINUS ASTERISK SLASH PERCENT EQUAL LPAREN RPAREN SEMICOLON CARET IDENTIFIER -------------------------------------------------------------------------------- From this we want to generate the following type _enum TokenKind_ and these enum constants: ---- CODE (type=c) ------------------------------------------------------------- enum TokenKind { EOI, BAD_TOKEN, HEX_LITERAL, OCT_LITERAL, DEC_LITERAL, PLUS, MINUS, ASTERISK, SLASH, PERCENT, EQUAL, LPAREN, RPAREN, SEMICOLON, CARET, IDENTIFIER, }; -------------------------------------------------------------------------------- This can be done with this program: ---- CODE (file=session20/fileio/make_tokenkind.c, fold) ----------------------- #include #include void printEnumHeader(FILE *out) { fprintf(out, "enum TokenKind\n"); fprintf(out, "{\n"); } void printEnumFooter(FILE *out) { fprintf(out, "};\n"); } void usage(const char *prg) { fprintf(stderr, "usage: %s input output\n", prg); exit(1); } int main(int argc, char *argv[]) { if (argc != 3) { usage(argv[0]); } FILE *in = fopen(argv[1], "r"); FILE *outEnum = fopen(argv[2], "w"); if (!in) { fprintf(stderr, "can not open input file '%s'\n", argv[1]); } if (!outEnum) { fprintf(stderr, "can not open output file '%s'\n", argv[2]); } printEnumHeader(outEnum); char *line = 0; size_t capacity = 0; ssize_t len; while ((len = getline(&line, &capacity, in)) > 0) { line[len - 1] = 0; fprintf(outEnum, "%*s%s,\n", 4, "", line); } free(line); printEnumFooter(outEnum); fclose(in); fclose(outEnum); } -------------------------------------------------------------------------------- Here how the demo: ---- SHELL (path=session20/fileio) --------------------------------------------- gcc -Wall -o make_tokenkind make_tokenkind.c ./make_tokenkind tokenkind.txt gen_tokenkind.h cat gen_tokenkind.h -------------------------------------------------------------------------------- Quiz 22: Generate _strTokenKind()_ ================================== Extend the program above so that it can be used as follows: ---- CODE (type=txt) ----------------------------------------------------------- ./make_tokenkind tokenkind.txt gen_tokenkind.h gen_strtokenkind.c -------------------------------------------------------------------------------- Compared to the program above a third output file can be specified to generate the source code for a function _strTokenKind()_ that returns a string representation for an enum constant. For _tokenkind.txt_ from above this would be the following: ---- CODE (type=c) ------------------------------------------------------------- const char * strTokenKind(enum TokenKind tokenKind) { switch (tokenKind) { case EOI: return "EOI"; case BAD_TOKEN: return "BAD_TOKEN"; case HEX_LITERAL: return "HEX_LITERAL"; case OCT_LITERAL: return "OCT_LITERAL"; case DEC_LITERAL: return "DEC_LITERAL"; case PLUS: return "PLUS"; case MINUS: return "MINUS"; case ASTERISK: return "ASTERISK"; case SLASH: return "SLASH"; case PERCENT: return "PERCENT"; case EQUAL: return "EQUAL"; case LPAREN: return "LPAREN"; case RPAREN: return "RPAREN"; case SEMICOLON: return "SEMICOLON"; case CARET: return "CARET"; case IDENTIFIER: return "IDENTIFIER"; default: fprintf(stderr, "internal error in strTokenKind: tokenKind = %d\n", tokenKind); exit(1); return ""; } } -------------------------------------------------------------------------------- Of course the generated source code should have proper indentation! Submit your program with ---- CODE (type=sh) ------------------------------------------------------------ submit hpc quiz22 make_tokenkind.c -------------------------------------------------------------------------------- Submit will compile your program and then generates output from a input file: ---- CODE (type=txt) ----------------------------------------------------------- gcc -Werror -Wall -Wcast-qual -o make_tokenkind make_tokenkind.c ./make_tokenkind tokenkind.txt gen_tokenkind.h gen_strtokenkind.c -------------------------------------------------------------------------------- It will then try to generate an object file with gcc -c tokenkind.c where ---- CODE (file=session20/tokenkind.h, fold) ----------------------------------- #ifndef TOKENKIND_H #define TOKENKIND_H #include "gen_tokenkind.h" const char *strTokenKind(enum TokenKind tokenKind); #endif // TOKENKIND_H -------------------------------------------------------------------------------- and ---- CODE (file=session20/tokenkind.c,fold) ------------------------------------ #include #include #include "tokenkind.h" #include "gen_strtokenkind.c" -------------------------------------------------------------------------------- will be used in addition.